thinkphp路由是php层解析request_uri的字符串匹配过程,发生在run()中route::dispatch()阶段,按注册顺序匹配规则并提取参数生成调度数组。

ThinkPHP 的路由机制不是靠 rewrite 或服务器配置硬转发,而是由框架在 PHP 层主动解析 $_SERVER['REQUEST_URI'](或 PATH_INFO)后,逐条比对预设规则,再决定调用哪个控制器方法——它本质是一次「字符串匹配 + 参数提取」的运行时决策过程。
路由解析发生在请求入口之后、控制器执行之前
框架启动后,think\App 实例会触发 run() 方法,其中关键一步是调用 Route::dispatch()。此时 HTTP 请求已进入 PHP,但控制器尚未实例化。路由组件会:
- 从
$_SERVER['REQUEST_URI']或$_SERVER['PATH_INFO']中截取路径部分(如/user/123/edit),去掉域名、查询参数和入口文件前缀 - 按注册顺序遍历所有路由规则(包括配置文件、动态注册、注解扫描结果)
- 对每个规则执行正则匹配或路径段比对;若命中,就提取变量(如
:id)、绑定 HTTP 方法(GET/POST)、合并默认参数 - 最终生成一个包含
controller、action、param的调度数组,交给后续的控制器调度器
Route::get() 和 Route::rule() 注册的规则存储位置不同
看似写法相似,但底层存储结构和匹配优先级有差异:
-
Route::get('user/:id', 'user/read')会把规则存入GET方法专属队列,只响应GET请求;同理post、put各自隔离 -
Route::rule('api/:version/<module>', 'api/index')->method('GET|POST')</module>存入通用规则池,匹配时需额外校验method字段 - 所有规则最终都归并到
think\Route类的静态属性$rules中,但分组索引不同,影响匹配顺序和性能
路由变量提取依赖正则捕获组,不是简单字符串替换
像 user/:id 这样的写法,框架内部会自动转成类似 ~^user/([^/]+?)/?$~ 的正则,并用 preg_match 提取捕获组内容。这意味着:
-
:id默认等价于([^/]+?),不支持中文或斜杠;要允许中文需显式定义变量规则:Route::pattern(['id' => '\w+']) - 多个变量连续出现(如
a/:x/b/:y)会被转为单个正则,中间的固定字符串(a、b)必须严格匹配,不能省略 - 如果路径中实际值含 URL 编码字符(如
%2F),需在匹配前手动urldecode(),否则正则无法识别
路由缓存不是自动生效的,且只缓存匹配结果
开启路由缓存('route_check' => true + 'route_annotation' => false)后,框架会把解析后的规则数组序列化写入 runtime/route.php。但要注意:
- 缓存文件只保存「路径 → 控制器/方法/参数」的映射关系,不包含任何逻辑判断或闭包回调
- 一旦修改了
route.php或使用了Route::any()这类动态注册,缓存就会失效,下次请求重新生成 - 注解路由(
@Route)默认不参与缓存,因为扫描类文件开销大,需手动设置'route_annotation' => true并配合php think optimize:route命令预编译
真正容易被忽略的是:路由匹配失败时,框架不会立即抛出 404,而是 fallback 到默认的 PATHINFO 模式(index.php/控制器/方法)。这意味着你看到的「页面存在」,可能根本没走你写的那条 Route::get() 规则,只是碰巧撞上了传统模式。验证是否真走路由,最直接的办法是关掉 'url_common' => false 并清空缓存重试。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











