欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

PHP內(nèi)置服務(wù)器實現(xiàn)URL重寫的實戰(zhàn)詳解

 更新時間:2025年09月12日 09:21:01   作者:tekin  
在PHP開發(fā)中,URL重寫是實現(xiàn)友好訪問路徑的核心手段,本文將從基礎(chǔ)原理,環(huán)境配置等維度,結(jié)合實際項目的重寫需求,提供一套可直接復(fù)用的解決方案,需要的小伙伴可以了解下

在PHP開發(fā)中,URL重寫是實現(xiàn)友好訪問路徑的核心手段(如將 /bazijp.html映射為 /?ac=bazijp),通常依賴Nginx/Apache的 rewrite指令。但本地開發(fā)時,PHP內(nèi)置服務(wù)器( php -S)更輕量高效,僅需通過自定義路由腳本即可實現(xiàn)等效重寫功能。本文將從基礎(chǔ)原理、環(huán)境配置、靜態(tài)資源兼容、復(fù)雜規(guī)則適配(如ThinkPHP,laravel項目)等維度,結(jié)合實際項目的重寫需求,提供一套可直接復(fù)用的解決方案,兼容PHP 5.6+主流版本。

一、核心原理:PHP內(nèi)置服務(wù)器的路由攔截機制

PHP內(nèi)置服務(wù)器本身不支持類似Nginx的rewrite語法,需通過路由腳本(如router.php)攔截所有請求并自定義轉(zhuǎn)發(fā)邏輯,核心流程如下:

  • 客戶端發(fā)起請求(如sm.tekin.cn/bazijp.html);
  • 內(nèi)置服務(wù)器將請求優(yōu)先轉(zhuǎn)發(fā)至路由腳本;
  • 路由腳本根據(jù)預(yù)設(shè)規(guī)則重寫URL(如映射為sm.tekin.cn/index.php?ac=bazijp);
  • 重寫后的請求轉(zhuǎn)發(fā)至項目統(tǒng)一入口(如public/index.php);
  • 若請求為靜態(tài)資源(CSS/JS/圖片),則直接返回文件,不進入重寫流程。

關(guān)鍵前提:內(nèi)置服務(wù)器對命令行參數(shù)順序有嚴格要求,必須遵循php -S [地址:端口] -t [文檔根目錄] [路由腳本],參數(shù)錯位會導(dǎo)致路由失效或靜態(tài)資源無法加載。

二、基礎(chǔ)環(huán)境配置:從啟動到調(diào)試(適配主流項目結(jié)構(gòu))

2.1 標準項目目錄結(jié)構(gòu)

以常見的“Web根目錄與業(yè)務(wù)代碼分離”結(jié)構(gòu)為例(如ThinkPHP、Laravel等框架通用),目錄結(jié)構(gòu)如下:

project-root/          # 項目根目錄
├─ public/             # Web根目錄(線上 訪問根路徑)
│  ├─ statics/         # 靜態(tài)資源目錄(CSS/JS/圖片)
│  └─ index.php        # 項目統(tǒng)一入口文件
└─ router.php          # URL重寫路由腳本

2.2 啟動PHP內(nèi)置服務(wù)器

項目根目錄執(zhí)行命令,指定public為Web根目錄,router.php為路由腳本,確保與線上環(huán)境目錄映射一致:

# 基礎(chǔ)啟動命令(本地訪問地址:http://127.0.0.1:8000)
php -S 127.0.0.1:8000 -t public router.php

# 啟用Xdebug調(diào)試(兼容PHP 5.6+)
php -dxdebug.remote_enable=1 -dxdebug.remote_autostart=1 -S 127.0.0.1:8000 -t public router.php

2.3 VS Code調(diào)試配置(launch.json)

若需在IDE中調(diào)試業(yè)務(wù)邏輯,需配置launch.json確保調(diào)試與重寫協(xié)同生效,關(guān)鍵在于參數(shù)順序與線上一致:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "PHP內(nèi)置服務(wù)器+URL重寫+調(diào)試",
            "type": "php",
            "runtimeExecutable": "/opt/local/bin/php56", // 本地PHP路徑(需與項目版本匹配)
            "request": "launch",
            "runtimeArgs": [
                "-dxdebug.remote_enable=1",
                "-dxdebug.remote_autostart=1",
                "-dxdebug.remote_port=9000", // 與php.ini中Xdebug端口一致
                "-S", "127.0.0.1:8000",
                "-t", "public", // 匹配線上Web根目錄
                "router.php"    // 路由腳本必須放在最后
            ],
            "cwd": "${workspaceRoot}",
            "serverReadyAction": {
                "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started",
                "uriFormat": "http://localhost:%s",
                "action": "openExternally" // 啟動后自動打開本地調(diào)試地址
            }
        }
    ]
}

三、基礎(chǔ)路由腳本:解決靜態(tài)資源與簡單重寫需求

3.1 核心痛點:靜態(tài)資源404與規(guī)則失效

本地開發(fā)中,兩類問題最為常見:

  • 靜態(tài)資源(如/statics/ffsm/global.css)被路由腳本攔截,返回404;
  • 簡單重寫規(guī)則(如/hehun.html/?ac=hehun)不生效,無法訪問目標模塊。

3.2 基礎(chǔ)版路由腳本(兼容PHP 5.6)

核心邏輯:優(yōu)先處理靜態(tài)資源,再執(zhí)行URL重寫,確保靜態(tài)資源加載與動態(tài)路由分離:

<?php
// 項目根目錄與Web根目錄定義
$projectRoot = __DIR__;
$webRoot = rtrim($projectRoot . '/public', '/') . '/';

// 1. 解析并標準化請求URI
$requestUri = $_SERVER['REQUEST_URI'];
$uriParts = parse_url($requestUri);
$uriPath = isset($uriParts['path']) ? $uriParts['path'] : '/';
$uriPath = preg_replace('#/\.\./#', '/', $uriPath); // 過濾目錄遍歷攻擊
$uriPath = rtrim($uriPath, '/'); // 統(tǒng)一去除末尾斜杠(如`/suanming/scbz/`→`/suanming/scbz`)
$uriPath = $uriPath === '' ? '/' : $uriPath;

// 2. 優(yōu)先處理靜態(tài)資源(存在則直接返回)
$requestedFile = $webRoot . ltrim($uriPath, '/');
if (file_exists($requestedFile) && is_file($requestedFile) && !is_dir($requestedFile)) {
    // 設(shè)置正確MIME類型,避免瀏覽器解析異常
    $mimeType = getMimeType($requestedFile);
    if ($mimeType) header("Content-Type: {$mimeType}");
    readfile($requestedFile);
    exit;
}

// 3. 定義基礎(chǔ)URL重寫規(guī)則(可根據(jù)項目需求擴展)
$rewriteRules = [
    '#^/index\.html$#'        => '/index.php', // 首頁規(guī)則
    '#^/bazijp\.html$#'        => '/?ac=bazijp', // 八字精批模塊規(guī)則
    '#^/hehun\.html$#'        => '/?ac=hehun', // 合婚模塊規(guī)則
    '#^/aboutus\.html$#'      => '/?ac=aboutus', // 關(guān)于我們頁面規(guī)則
    '#^/xyd-([0-9]+)\.html$#' => '/?ac=xyd&id=$1', // 詳情頁動態(tài)規(guī)則
    '#^/([^/]+)\.html$#'              => '/index.php?ac=$1', // 最后執(zhí)行:通用.html規(guī)則(最寬泛,避免覆蓋前面的具體規(guī)則)
];

// 4. 應(yīng)用重寫規(guī)則
$newUri = $uriPath;
foreach ($rewriteRules as $pattern => $target) {
    if (preg_match($pattern, $uriPath)) {
        $newUri = preg_replace($pattern, $target, $uriPath);
        break; // 匹配到即終止,避免規(guī)則沖突
    }
}

// 5. 處理查詢參數(shù)(保留原參數(shù),新規(guī)則參數(shù)覆蓋同名原參數(shù))
$originalQuery = isset($uriParts['query']) ? $uriParts['query'] : '';
$newUriParts = parse_url($newUri);
$newPath = isset($newUriParts['path']) ? $newUriParts['path'] : '/';
$newQuery = isset($newUriParts['query']) ? $newUriParts['query'] : '';

$finalQuery = '';
if (!empty($originalQuery) && !empty($newQuery)) {
    parse_str($originalQuery, $originalParams);
    parse_str($newQuery, $newParams);
    $mergedParams = array_merge($originalParams, $newParams);
    $finalQuery = http_build_query($mergedParams);
} elseif (!empty($originalQuery)) {
    $finalQuery = $originalQuery;
} else {
    $finalQuery = $newQuery;
}

// 6. 更新服務(wù)器變量,轉(zhuǎn)發(fā)到統(tǒng)一入口
$finalUri = $newPath . ($finalQuery ? "?{$finalQuery}" : '');
$_SERVER['REQUEST_URI'] = $finalUri;
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['QUERY_STRING'] = $finalQuery;
parse_str($finalQuery, $_GET); // 同步更新GET參數(shù),適配框架參數(shù)獲取

// 7. 執(zhí)行入口文件
$indexFile = $webRoot . 'index.php';
if (file_exists($indexFile)) {
    include_once $indexFile;
} else {
    http_response_code(404);
    echo "404 Not Found:public/index.php 入口文件不存在";
}
exit;

/**
 * 兼容PHP 5.6的MIME類型獲取
 * @param string $file 文件路徑
 * @return string|null MIME類型
 */
function getMimeType($file) {
    $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
    $mimeMap = [
        'css' => 'text/css', 'js' => 'application/javascript',
        'html' => 'text/html', 'jpg' => 'image/jpeg',
        'png' => 'image/png', 'gif' => 'image/gif', 'ico' => 'image/x-icon'
    ];
    return isset($mimeMap[$extension]) ? $mimeMap[$extension] : null;
}

3.3 基礎(chǔ)規(guī)則測試

  • 訪問http://127.0.0.1:8000/bazijp.html,通過var_dump($_GET)應(yīng)看到array('ac' => 'bazijp');
  • 訪問http://127.0.0.1:8000/xyd-123.html,應(yīng)看到array('ac' => 'xyd', 'id' => '123')
  • 訪問http://127.0.0.1:8000/statics/ffsm/global.css,應(yīng)直接返回CSS文件內(nèi)容。

四、進階:適配復(fù)雜重寫規(guī)則(以ThinkPHP項目為例)

實際項目中,常需處理大量復(fù)雜重寫規(guī)則(如多模塊路由、動態(tài)參數(shù)拼接)。例如某命理類項目的Nginx規(guī)則片段:

rewrite ^/aboutus.html /index.php?ac=aboutus last;
rewrite ^/xyd-([0-9]+).html /index.php?ac=xyd&id=$1 last;
rewrite ^/(.*).html /index.php?ac=$1 last;
rewrite ^/show-([0-9]+).html /index.php?ct=news&ac=show&id=$1;

這類規(guī)則遷移時,核心挑戰(zhàn)是避免通用規(guī)則覆蓋具體規(guī)則(如/aboutus.html不能被/.+\.html的通用規(guī)則錯誤映射)。

4.1 復(fù)雜規(guī)則適配方案:規(guī)則分組排序

核心思路:將規(guī)則按“精準度”分組,精準規(guī)則優(yōu)先匹配,通用規(guī)則兜底,確保每個模塊的路由邏輯與線上一致。

完整路由腳本(適配復(fù)雜項目規(guī)則)

<?php
$projectRoot = __DIR__;
$webRoot = rtrim($projectRoot . '/public', '/') . '/';

// 1. 標準化請求URI
$requestUri = $_SERVER['REQUEST_URI'];
$uriParts = parse_url($requestUri);
$uriPath = isset($uriParts['path']) ? $uriParts['path'] : '/';
$uriPath = preg_replace('#/\.\./#', '/', $uriPath);
$uriPath = rtrim($uriPath, '/');
$uriPath = $uriPath === '' ? '/' : $uriPath;

// 2. 優(yōu)先處理靜態(tài)資源
$requestedFile = $webRoot . ltrim($uriPath, '/');
if (file_exists($requestedFile) && is_file($requestedFile) && !is_dir($requestedFile)) {
    $mimeType = getMimeType($requestedFile);
    if ($mimeType) header("Content-Type: {$mimeType}");
    readfile($requestedFile);
    exit;
}

// 3. 規(guī)則分組:精準規(guī)則 → 通用規(guī)則(避免寬覆蓋窄)
// 注意下面的規(guī)則需要根據(jù)你自己的項目進行修改,這里僅做示例 更多可以參考 https://sm.tekin.cn 站點的URL重寫
// --------------------------
// 第一組:精準規(guī)則(無動態(tài)參數(shù),固定URL)
// --------------------------
$exactRules = [
    // 基礎(chǔ)頁面
    '#^/index\.html$#'                => '/index.php',
    '#^/aboutus\.html$#'              => '/index.php?ac=aboutus',
    '#^/contactus\.html$#'            => '/index.php?ac=contactus',
];

// --------------------------
// 第二組:通用規(guī)則(帶動態(tài)參數(shù)、后綴匹配)
// --------------------------
$generalRules = [
    // 帶ID的精準后綴規(guī)則
    '#^/xyd-([0-9]+)\.html$#'         => '/index.php?ac=xyd&id=$1',
    // 姓名模塊動態(tài)路徑
    '#^/xmfx/([^/]+)$#'      => '/index.php?ct=xingming&ac=xmfx&name=$1',
    '#^/xqlist-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$#' => '/index.php?ct=xq&xid=$1&sex=$2&hs=$3&page=$4',
    // 最后執(zhí)行:通用.html規(guī)則(兜底)
    '#^/([^/]+)\.html$#'              => '/index.php?ac=$1',
];

// 4. 執(zhí)行重寫:先精準后通用
$newUri = $uriPath;
$ruleMatched = false;

// 第一步:匹配精準規(guī)則(核心業(yè)務(wù)優(yōu)先)
foreach ($exactRules as $pattern => $target) {
    if (preg_match($pattern, $uriPath)) {
        $newUri = preg_replace($pattern, $target, $uriPath);
        $ruleMatched = true;
        break;
    }
}

// 第二步:精準規(guī)則未匹配時,匹配通用規(guī)則
if (!$ruleMatched) {
    foreach ($generalRules as $pattern => $target) {
        if (preg_match($pattern, $uriPath)) {
            $newUri = preg_replace($pattern, $target, $uriPath);
            break;
        }
    }
}

// 5. 合并查詢參數(shù)(同基礎(chǔ)版邏輯)
$originalQuery = isset($uriParts['query']) ? $uriParts['query'] : '';
$newUriParts = parse_url($newUri);
$newPath = isset($newUriParts['path']) ? $newUriParts['path'] : '/';
$newQuery = isset($newUriParts['query']) ? $newUriParts['query'] : '';

$finalQuery = '';
if (!empty($originalQuery) && !empty($newQuery)) {
    parse_str($originalQuery, $originalParams);
    parse_str($newQuery, $newParams);
    $mergedParams = array_merge($originalParams, $newParams);
    $finalQuery = http_build_query($mergedParams);
} elseif (!empty($originalQuery)) {
    $finalQuery = $originalQuery;
} else {
    $finalQuery = $newQuery;
}

// 6. 轉(zhuǎn)發(fā)到入口文件
$finalUri = $newPath . ($finalQuery ? "?{$finalQuery}" : '');
$_SERVER['REQUEST_URI'] = $finalUri;
$_SERVER['SCRIPT_NAME'] = '/index.php';
$_SERVER['QUERY_STRING'] = $finalQuery;
parse_str($finalQuery, $_GET);

$indexFile = $webRoot . 'index.php';
if (file_exists($indexFile)) {
    include_once $indexFile;
} else {
    http_response_code(404);
    echo "404 Not Found:public/index.php 入口文件不存在";
}
exit;

// MIME類型函數(shù)(同基礎(chǔ)版)
function getMimeType($file) {
    $extension = strtolower(pathinfo($file, PATHINFO_EXTENSION));
    $mimeMap = [
        'css' => 'text/css', 'js' => 'application/javascript',
        'html' => 'text/html', 'jpg' => 'image/jpeg',
        'png' => 'image/png', 'gif' => 'image/gif', 'ico' => 'image/x-icon'
    ];
    return isset($mimeMap[$extension]) ? $mimeMap[$extension] : null;
}
?>

4.2 規(guī)則適配關(guān)鍵點

  • 分組邏輯$exactRules存放固定URL(如/aboutus.html),$generalRules存放動態(tài)URL(如/([^/]+)\.html),確保精準規(guī)則不被覆蓋;
  • 通用規(guī)則內(nèi)部順序:即使在$generalRules中,也需從“具體”到“寬泛”排序(如先匹配/xyd-([0-9]+)\.html,再匹配/([^/]+)\.html);
  • 參數(shù)兼容性:保留原請求中的查詢參數(shù)(如/user/abc?foo=bar/index.php?ct=user&ac=abc&foo=bar),符合線上重寫習(xí)慣。

五、常見問題與解決方案

5.1 靜態(tài)資源404

原因:路由腳本未優(yōu)先處理靜態(tài)資源,或$webRoot路徑拼接錯誤(如多寫斜杠);

解決方案:確保靜態(tài)資源判斷邏輯在重寫規(guī)則之前,$requestedFile路徑格式為public/statics/ffsm/global.css。

5.2 重寫規(guī)則不生效

原因:內(nèi)置服務(wù)器參數(shù)順序錯誤(如router.php放在-t public之前),或正則表達式錯誤(如.未轉(zhuǎn)義);

解決方案:嚴格遵循php -S 地址 -t 根目錄 路由腳本,正則使用#^/aboutus\.html$#格式。

5.3 PHP 5.6兼容性問題

問題:使用??空合并運算符導(dǎo)致語法錯誤;

解決方案:用isset()+三元運算符替代(如$uriPath = isset($uriParts['path']) ? $uriParts['path'] : '/')。

六、總結(jié)

PHP內(nèi)置服務(wù)器的URL重寫核心在于路由腳本的設(shè)計,通過“靜態(tài)資源優(yōu)先+規(guī)則分組排序”的思路,可實現(xiàn)與Nginx/Apache等效的重寫功能。關(guān)鍵要點如下:

  • 參數(shù)順序:啟動時嚴格遵循-S 地址 -t 根目錄 路由腳本;
  • 靜態(tài)優(yōu)先:避免靜態(tài)資源進入重寫流程;
  • 規(guī)則分組:按“精準→通用”排序,解決規(guī)則覆蓋問題;
  • 版本兼容:針對PHP 5.6等舊版本調(diào)整語法,確保項目可用性。

這套方案可直接復(fù)用于ThinkPHP等主流框架的本地開發(fā),無需依賴重型Web服務(wù)器,有效提升開發(fā)效率,減少環(huán)境差異導(dǎo)致的問題。

到此這篇關(guān)于 PHP內(nèi)置服務(wù)器實現(xiàn)URL重寫的實戰(zhàn)詳解的文章就介紹到這了,更多相關(guān) PHP URL重寫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評論