在現代 PHP 應用開發中,乾淨的 URL 結構與請求分發機制是基石。本文將帶你從零構建一個輕量級的 RESTful 路由類,涵蓋路由表定義、HTTP 方法匹配、動態參數提取、控制器自動載入與反射調用等核心功能。
$_SERVER 獲取當前請求的 URI 與 HTTP 方法。:param 風格的動態參數。<?php
class Router {
/** @var array 路由表 */
private $routers = [
['name' => 'userlist', 'pattern' => 'get /user', 'action' => 'User#get'],
['name' => 'userinfo', 'pattern' => 'get /user/:id', 'action' => 'User#getById'],
['name' => 'useradd', 'pattern' => 'post /user', 'action' => 'User#add'],
['name' => 'userupdate','pattern' => 'put /user/:id', 'action' => 'User#update'],
['name' => 'userdel', 'pattern' => 'delete /user/:id','action' => 'User#delete'],
];
/** 入口:分發當前請求 */
public function dispatch() {
$url = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];
foreach ($this->routers as $router) {
[$patternMethod, $patternUri] = explode(' ', $router['pattern'], 2);
if (strcasecmp($patternMethod, $method) !== 0) continue;
$params = $this->checkUrl($method, strtolower($url), strtolower($patternUri));
if ($params !== null) {
array_shift($params); // 移除正則匹配的完整字串
return $this->invoke($router['action'], $params);
}
}
// 無匹配路由
http_response_code(404);
echo '404 Not Found';
}
/** 透過反射調用控制器動作 */
private function invoke(string $action, array $params) {
[$className, $methodName] = explode('#', $action);
$className .= 'Action'; // 慣例:控制器類名以 Action 結尾
$file = __DIR__ . DIRECTORY_SEPARATOR . 'action' . DIRECTORY_SEPARATOR . $className . '.php';
if (!file_exists($file)) {
http_response_code(404);
echo "404: Controller $className not found";
return;
}
require $file;
$refClass = new ReflectionClass($className);
if (!$refClass->hasMethod($methodName)) {
http_response_code(404);
echo "404: Method $methodName not found in $className";
return;
}
$instance = $refClass->newInstance();
$refMethod = $refClass->getMethod($methodName);
$refMethod->invokeArgs($instance, $params);
}
/** 正則匹配 URI 並提取參數 */
private function checkUrl(string $method, string $url, string $pattern): ?array {
// 將 /user/:id 轉換為正則:/user/([^/]+)
$pattern = '/' . str_replace('/:', '/([^/]+)', trim($pattern, '/')) . '/?$';
if (preg_match($pattern, $url, $matches)) {
return $matches;
}
return null;
}
}
// 使用範例
$router = new Router();
$router->dispatch();
?>
路由模式中的 :id 會被轉換為正則捕獲組 ([^/]+),匹配非斜線字符,提取出的值將按順序傳遞給控制器方法。
使用 ReflectionClass 動態實例化控制器並調用方法,避免硬編碼依賴,符合依賴倒置原則。
提供清晰的 404 回應:路由不存在、控制器檔案缺失、方法未定義等情況皆有對應處理。
dispatch() 前後掛載前置/後置處理器(如認證、日誌、CORS)。/api/v1 等前綴分組,簡化大型應用路由管理。url('userinfo', ['id' => 123]) 輔助函數生成反向連結。這個路由器雖然精簡,但已具備現代 PHP 框架路由系統的核心骨架。理解其運作原理,有助於你在使用 Laravel、Slim 或 Symfony 等框架時,更深入地掌握其底層機制,也能在輕量專案中直接落地使用。