首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP URL 路由映射實戰:從請求解析到控制器分發的輕量級實現

     編輯:PHP 綜合知識

背景

現代 PHP 框架(Laravel、Symfony、ThinkPHP、Zend Framework)均採用「單一入口 + 路由分發」架構。本文實現一個極簡的兩階段路由器:

  1. 解析階段:從 REQUEST_URI / QUERY_STRING 提取 controller、action、params
  2. 分發階段:根據約定載入控制器類、注入參數、調用動作方法

代碼參考 Zend Framework 1 風格,適合學習原理或輕量級項目直接使用。

階段一:URL 解析器 url_router()

<?php
/**
 * 解析當前請求的路由信息
 * 支持三種 URL 形式:
 *   /path/to/index.php/controller/action/param/value
 *   /path/to/index.php?/controller/action/param/value
 *   /path/to/site/?/controller/action/param/value
 * @return array ['controller'=>..., 'action'=>..., 'params'=>[...]]
 */
function url_router(): array
{
    $path = strip_tags($_SERVER['REQUEST_URI']); // 防 XSS
    $pos  = strpos($path, '.php');

    if ($pos !== false) {
        // 情況 1 & 2:有 .php,取其後部分
        $path = substr($path, $pos + 4);
    } else {
        // 情況 3:無 .php(如 PATH_INFO 模式或重寫後)
        if (empty($_SERVER['QUERY_STRING'])) {
            $qPos = strpos($path, '?');
            if ($qPos !== false) {
                $path = substr($path, $qPos + 1);
            } else {
                $path = '';
            }
        } else {
            $path = $_SERVER['QUERY_STRING'];
        }
    }

    // 統一去除首部 /
    if ($path !== '' && $path[0] === '/') {
        $path = substr($path, 1);
    }

    // 默認路由
    $router = [
        'controller' => 'index',
        'action'     => 'index',
        'params'     => [],
    ];

    if ($path !== '') {
        $parts = explode('/', $path);
        $router['controller'] = $parts[0] ?? 'index';
        $router['action']     = $parts[1] ?? 'index';

        // 其餘成對作為參數
        $params = [];
        for ($i = 2; $i < count($parts); $i += 2) {
            $key   = $parts[$i] ?? '';
            $value = $parts[$i + 1] ?? '';
            if ($key !== '') $params[$key] = $value;
        }
        $router['params'] = $params;
    }

    return $router;
}
?>

階段二:控制器分發器 url_dispatch()

<?php
/**
 * 根據路由信息分發到控制器
 * @param array  $router    url_router() 返回的陣列
 * @param string $appPath   控制器目錄相對項目根目錄路徑
 * @return bool
 */
function url_dispatch(array $router, string $appPath = '/app/controllers/'): bool
{
    // 1. 載入基礎控制器類(假設項目有統一基類)
    require_once SERVER_PATH . '/libs/controller.class.php';

    // 2. 構建控制器類名:首字母大寫 + Controller 後綴
    $className = ucfirst($router['controller']) . 'Controller';
    $filePath  = SERVER_PATH . $appPath . $className . '.class.php';

    if (!file_exists($filePath)) {
        die('缺少必要的控制器類:' . $className);
    }

    require_once $filePath;

    // 3. 實例化並注入參數
    $controller = new $className();
    if (method_exists($controller, '_setParam')) {
        $controller->_setParam($router['params']);
    }

    // 4. 調用動作方法:action 名 + Action 後綴
    $actionMethod = $router['action'] . 'Action';
    if (!method_exists($controller, $actionMethod)) {
        die('動作方法不存在:' . $actionMethod);
    }

    $controller->$actionMethod();
    return true;
}
?>

入口文件 index.php 整合

<?php
define('SERVER_PATH', __DIR__); // 項目根目錄

require_once 'router.php'; // 包含上述兩個函數

// 1. 解析路由
$router = url_router();

// 2. 分發執行
url_dispatch($router);

// 3. 程序結束(控制器內部負責輸出)
?>

控制器基類示例 controller.class.php

<?php
abstract class Controller
{
    protected $params = [];

    public function _setParam(array $params)
    {
        $this->params = $params;
    }

    protected function param(string $key, $default = null)
    {
        return $this->params[$key] ?? $default;
    }

    protected function view(string $template, array $data = [])
    {
        extract($data);
        ob_start();
        include VIEW_PATH . '/' . $template . '.php';
        return ob_get_clean();
    }
}
?>

實際控制器 app/controllers/IndexController.class.php

<?php
class IndexController extends Controller
{
    public function indexAction()
    {
        $name = $this->param('name', 'Guest');
        echo $this->view('welcome', ['name' => $name]);
    }

    public function helloAction()
    {
        echo "Hello, " . $this->param('name', 'World');
    }
}
?>

URL 訪問對照表

訪問 URLcontrolleractionparams
/index.php/index/indexindexindex[]
/index.php/index/hello/name/Johnindexhello['name' => 'John']
/?/index/hello/name/Johnindexhello['name' => 'John']
/index.php?/index/hello/name/Johnindexhello['name' => 'John']

擴展方向

  1. 路由組/前綴:/admin/user/list → controller=admin_user 或命名空間 Admin\UserController
  2. 正則/RESTful 路由:引入 FastRoute 或自寫正則匹配表,支持 GET /user/{id:\d+} 參數綁定
  3. 中間件管道:分發前/後執行認證、日誌、CSRF、限流等中間件
  4. 依賴注入容器:控制器實例化改由容器管理,支持構造函數注入 Service
  5. 異常處理:統一 try/catch 捕獲 HttpException(404, 500) 渲染錯誤頁

總結

僅用兩個函數(url_router + url_dispatch)即可構建出一個功能完整的 MVC 分發核心。理解「請求解析 → 路由匹配 → 控制器實例化 → 動作調用」這一流程,是掌握所有現代 PHP 框架內部機制的鑰匙。輕量級項目可直接使用本代碼;中大型項目建議直接接入成熟路由組件(nikic/fast-route、league/route、框架內置),專注業務邏輯開發。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved