PHP路由本质是将$_SERVER['REQUEST_URI']按规则映射到函数或方法,需Web服务器重写所有非静态请求至index.php,并通过解析路径、正则匹配提取控制器/动作/参数,路由应专注分发,权限校验等交由中间件处理。

PHP 路由不是框架“自带的魔法”,而是你主动设计的一套请求分发逻辑——它本质是把 $_SERVER['REQUEST_URI'] 这个字符串,按规则映射到某个函数或控制器方法上执行。
入口文件怎么接管所有请求?
Web 服务器(Apache/Nginx)必须把非静态资源的请求全部转给 index.php,否则路由代码根本没机会运行。
- Apache:靠
.htaccess开启重写RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] - Nginx:用
try_files指令兜底location / { try_files $uri $uri/ /index.php?$query_string; }
⚠️ 容易踩的坑:
- 忘记开启 Apache 的
mod_rewrite,导致 404 或直接暴露 PHP 文件 - Nginx 配置里漏了
$query_string,GET 参数丢失 - 静态资源(CSS/JS/图片)被错误重写,造成加载失败 → 必须加
!-f和!-d判断
怎么从 URL 提取控制器和方法?
原始路径如 /user/profile/123?tab=posts,得先标准化再拆解:
立即学习“PHP免费学习笔记(深入)”;
- 去掉查询参数:
parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) - 去掉入口脚本名(如
/index.php)避免干扰 - 按
/分割,跳过空段(开头斜杠会导致首项为空)
常见做法是约定前两段为 控制器/动作,其余为参数:
$parts = array_filter(explode('/', $path));
$controller = $parts[0] ?? 'Home';
$action = $parts[1] ?? 'index';
$params = array_slice($parts, 2);⚠️ 注意点:
Android应用框架原理与程序设计36技 pdf繁体版,书籍内容适用于Android 1.0,有些朋友可能对Android还不太熟悉吧?不知您是否听说过Google 在HTC定制的高端手机呢?其操作系统是基于Android的,如果还是不太清楚的话,可以Google一下“HTC g2”手机,可以大致了解一下手机操作系统的界面及架构特点。不管怎么说,Android手机编程目前还是主要面向高端,在将来可能会普及,因此Android编程还是很有必要掌握的。
- 不要直接用
$_GET拆路径,那是查询参数,不是路由参数 -
array_filter()必须加,否则['', 'user', 'profile']会错位 - 如果路径是
/api/v1/users,硬拆成三段就崩了 → 动态路由需正则匹配,不能只靠explode
为什么正则路由比静态数组更实用?
静态数组(['/user' => 'UserController@index'])只适合 demo。真实项目要支持:
-
/user/123→ 提取id=123 -
/post/hello-world→ 提取slug=hello-world -
/api/v2/users→ 匹配前缀并透传版本号
所以得用正则逐条匹配:
$routes = [ '#^/user/(\d+)$#' => ['UserController', 'show'], '#^/post/([a-z0-9\-]+)$#' => ['PostController', 'view'], ];foreach ($routes as $pattern => [$ctrl, $act]) { if (preg_match($pattern, $path, $matches)) { array_shift($matches); // 去掉完整匹配项 $params = $matches; require "controllers/{$ctrl}.php"; (new $ctrl())->$act(...$params); exit; } }
⚠️ 关键细节:
- 正则必须用
#或~做分隔符,避免和 URL 中的/冲突 -
preg_match返回的是匹配结果数组,$matches[0]是全量匹配,真正参数从$matches[1]开始 - 没匹配成功时别忘了
http_response_code(404),否则默认返回 200 + 空白页
路由性能差,是不是该换 trie 树?
简单项目用 foreach + 正则完全够用;但当路由规则超 50 条、QPS 上千时,线性遍历确实成瓶颈。
- trie(字典树)适合前缀匹配,比如
/api/v1/、/api/v2/共享节点 - radix 树(如 Gin、Echo 用的)还能压缩公共路径,匹配更快
- 但 PHP 原生没内置,自己实现容易出 bug;小项目不如加一层 APCu 缓存编译后的正则
// 缓存一次解析结果,避免重复 preg_match
$cacheKey = md5($path);
if ($cached = apcu_fetch($cacheKey)) {
[$ctrl, $act, $params] = $cached;
} else {
// 执行匹配逻辑...
apcu_store($cacheKey, [$ctrl, $act, $params], 60);
}真正卡顿往往不在匹配本身,而在:
- 每次请求都 require 控制器文件(应提前 autoload)
- 路由里做了 DB 查询或远程调用(应移入控制器,路由只做分发)
- 忘记 exit/return,后续代码继续执行造成意外输出
路由最常被忽略的,是它不该承担权限校验、日志记录、输入过滤——这些是中间件的事。把它当成纯粹的“URL→函数”调度器,结构才清晰、才好测、才不会越写越重。










