今天啃关于路由解析的部分,感受这块仍是挺复杂的;有的点仍是没看透,把看明白的总结出来。php
咱们在使用路由解析的时候,不少部分参与了路由解析,远不止tp框架以下图
能够看出从客户端发起到服务器处理响应,经理的4个阶段,tp框架只是其中一部分。html
url做为一种输入的数据,过路由解析,
匹配到应用业务控制器(也有多是闭包函数和自定义的类)thinkphp
path_info字符串标志,path_info兼容内容,path_info分隔符
'var_pathinfo' => 's',
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
'pathinfo_depr' => '/',
系统变量request_uri,系统变量base_url
'url_request_uri' => 'REQUEST_URI',
'base_url' => $_SERVER["SCRIPT_NAME"],
伪静态后缀,普通方式参数?,禁止访问后缀
'url_html_suffix' => '.html',
'url_common_param' => false,
'url_deny_suffix' => 'ico|png|gif|jpg',数组
路由开启与关闭,强制路由开启与关闭,模块映射
'url_route_on' => true,
'url_route_must' => false,
'url_module_map' => [],
域名部署开启与关闭,根域名
'url_domain_deploy' => false,
'url_domain_root' => '',
控制器自动转换开启与关闭,操做自动转换开启与关闭
'url_controller_convert' => true,
'url_action_convert' => true,
// 按照顺序解析变量
'url_param_type' => 1,
//路由配置文件,可配置多个
'route_config_file' => ['route'],缓存
这里只简单举两个栗子,想知道更详细请查阅thinkphp5.0官方手册毕竟这里咱们是以分析源码为主。服务器
我将tp5的路由解析是将咱们配置文件中配置的路由规则注册进thinkRoute.php的私有静态变量$rules,本质上来讲检验路由就是按规则将$rules中的信息进行校验, 因此为了便于理解,我将讲述分为两部分, 一部分是路由注册, 一部分是路由解析;下面将会根据这两部分进行分析。闭包
整个过程以下图
入口
app::run==>app::routeCheck===>route::import
在routeCheck中会检查是否开启路由缓存,同时会去RUNTIME_PATH下查找是否有缓存的路由文件,若是没有引入文入路由文件,在本例中便是route .php 当引入app
会执行其中标红的部分,返回的数组,会执行Route::import 方法,而在注册路由的过程当中除了分组路由之外(咱们另外分析),最后都是对Route::setRule的封装,因此主要分析一下setRule
首先介绍一下Route的$rules的结构
其中get到options位置都是类似的,一个整的注册相似与框架
rule 为路由表达式 route为匹配路由路径 var 为指定参数 其中key为参数名 value的值 有1 或2 其中1 是必须添加 2为选填 option 为的值路由参数中说起的值(不明白请查阅文档) pattern 为次路径下的参数限制
另外请注意,若是在注册路由时,指定的方式传类型
会有一个优化(在我理解)
会将路由直接对应一个true,而其对应的参数在$rules中的*中存储dom
protected static function setRule($rule, $route, $type \= '\*', $option \= \[\], $pattern \= \[\], $group \= '') { ~~~~ if (is\_array($rule)) { // 是不是批量注册 $name \= $rule\[0\]; $rule \= $rule\[1\]; } elseif (is\_string($route)) { $name \= $route; } if (!isset($option\['complete\_match'\])) { // 注册规则中是否有彻底匹配 if (Config::get('route\_complete\_match')) { //配置文件中是否有彻底匹配 $option\['complete\_match'\] = true; } elseif ('$' \== substr($rule, \-1, 1)) { // 注册路由表达式中是否包含结束副 // 是否完整匹配 $option\['complete\_match'\] = true; } } elseif (empty($option\['complete\_match'\]) && '$' \== substr($rule, \-1, 1)) { // 是否完整匹配 $option\['complete\_match'\] = true; } if ('$' \== substr($rule, \-1, 1)) { //去掉表达式中的结束符 $rule \= substr($rule, 0, \-1); } if ('/' != $rule || $group) { $rule \= trim($rule, '/'); } $vars \= self::parseVar($rule); // 提取表达式中的参数设定 if (isset($name)) { $key \= $group ? $group . ($rule ? '/' . $rule : '') : $rule; $suffix \= isset($option\['ext'\]) ? $option\['ext'\] : null; self::name($name, \[$key, $vars, self::$domain, $suffix\]); /*注册$rules的name成员, key 为对应的映射地址 0 ==》 路由表达式 1 ==》 参数列表 2 ==》 域名 4 ==》 后缀 同时一个映射地址能够存储多个 */ } if (isset($option\['modular'\])) { $route \= $option\['modular'\] . '/' . $route; } if ($group) { //是否设置分组 if ('\*' != $type) { //记录是哪一种方式 $option\['method'\] = $type; } //是否设置包含域名 if (self::$domain) { self::$rules\['domain'\]\[self::$domain\]\['\*'\]\[$group\]\['rule'\]\[\] = \['rule' \=> $rule, 'route' \=> $route, 'var' \=> $vars, 'option' \=> $option, 'pattern' \=> $pattern\]; } else { self::$rules\['\*'\]\[$group\]\['rule'\]\[\] = \['rule' \=> $rule, 'route' \=> $route, 'var' \=> $vars, 'option' \=> $option, 'pattern' \=> $pattern\]; } } else { if ('\*' != $type && isset(self::$rules\['\*'\]\[$rule\])) { unset(self::$rules\['\*'\]\[$rule\]); } if (self::$domain) { self::$rules\['domain'\]\[self::$domain\]\[$type\]\[$rule\] = \['rule' \=> $rule, 'route' \=> $route, 'var' \=> $vars, 'option' \=> $option, 'pattern' \=> $pattern\]; } else { self::$rules\[$type\]\[$rule\] = \['rule' \=> $rule, 'route' \=> $route, 'var' \=> $vars, 'option' \=> $option, 'pattern' \=> $pattern\]; //注册对应值 } if ('\*' \== $type) { // 注册路由快捷方式 foreach (\['get', 'post', 'put', 'delete', 'patch', 'head', 'options'\] as $method) { if (self::$domain && !isset(self::$rules\['domain'\]\[self::$domain\]\[$method\]\[$rule\])) { self::$rules\['domain'\]\[self::$domain\]\[$method\]\[$rule\] = true; } elseif (!self::$domain && !isset(self::$rules\[$method\]\[$rule\])) { self::$rules\[$method\]\[$rule\] = true; } } } }}
到这里为止,咱们经过两种方式进行了路由到注册
1 引用的Route.php 中经过使用Route::rule,Route::get等方法注册的
3 经过Route::import导入配置文件中的批量配置
路由检测主要集中在check方法内 主要过程是
静态检测
路由规则检测
也就是一下6部分
1 检查路由缓存
若开启 route_check_cache 时,则在第一次缓存后将匹配成功的路由参数
存储在缓存中
直接调用Route::parseRule进行解析
其实在check的最后检查过5项后最后第六步也会调用parserule进行路由转换,这里就一并分析了
简单分析下parserule关键的地方
(1)闭包注册
判断是否为闭包类 若是是result返回闭包对象
(2)若是路由是到重定向地址
tp5路由规则中 首字符为/ 或包含http://既为重定向地址
若是是则返回result 包含当前的路由地址,若是路由没有设置status参数则默认为301
(3)路由到方法
tp5 的路由到方法的格式为
路由地址必须从跟命名空间开始,因此开头必须为'\'
这里处理将$route 中的@替换回来
提取@后的method
最后返回result
(4)路由到控制器操做
当首字符为@的时候则定义到控制器方法,
这里除了解析字符串返回$result对象意外
会调用Request->Action执行对应的方法
(5)路由到普通的 既模块/控制器/操做
最后在解析完成后 Route::parseRule 会返回一个result数组,为路由的调用提供依据。
2 检测路由别名
别名的设置以下
咱们能够看到 若是别名对应存储的不是一个字符串地址,而是参数数组,则会依次检查参数的是否符合
后面依次是参数有效性检查 ; 检测别名对应地址,匹配其路由到类,路由到控制器,路由到模块/控制器,的需求
最后若是匹配成功,返回result数组,若是匹配失败则返回false,执行其他步骤
3 检测域名部署
这里也将域名的注册 和域名的检查同时介绍
域名注册:
域名能够绑定的类型有三种
下图为注册函数
能够看见domain函数会直接进行注册,将路由地址,路由参数,参数规则
这里我以为闭包函数的规则有点鸡肋
1 执行时机
竟然是在执行注册的时候就进行调用,我不明白这样的调用有什么意义
2 另外只执行了调用却没有对闭包的任何参数进行注册。
闭包存在的意义就只有分别对各自域名进行注册
但是因为这个闭包注册是定义在配置文件中的,那样就是说不论我是访问哪个子域名,我仍是要加载全部的闭包注册。因为这些缘由因此感受仍是比较鸡肋的。
而动态注册规则
最终会执行
将参数的规则除了在name中
另外注册一份在domain中,结构以下图
域名检测
对域名进行解析
得到当前路由规则中对应的值
若是对应的规则包含'['bind']' 说明不是动态注册规则,则在解析后,将路由规则注册进$bind私有成员变量
若是不包含说明是动态注册规则
则替换目前的规则
4 检测URL绑定
这里就是处理上一步中域名绑定的规则,将其处理为result返回跳转
5 静态路由规则检测
静态路由是指,访问连接和注册连接一致
6 路由规则检测
路由规则检测最后部分是调用的是Route::parseRule,这一部分在1中已经分析过,在这里就不重复了,这里只分析Route::checkRoute层面上作了什么
1 遍历当前访问方式的rules
当前访问方式的规则数组rules,遍历当前访问方式的路由规则
若是是分组路由则递归调用自身,并将分组参数传入
也就是说咱们注册时填充的路由分组,到这里会统一将分组名称填写彻底后进行路由检测
执行前置的参数检查,最后在C:wamp64wwwthinkphplibrarythinkRoute::match中执行路由的参数,和当前url 是否符合路由的检测
检测$m2 为当前路由规则
$m1 为当前url
从注册中能够看出路由规则 和咱们当前的访问url 除开参数意外,应该是一一对应的,代码中为1 检测是否为变量2 检测是否为可选参数,若是是,去掉[]并将表明是否为可选参数的变量置为响应的值3 执行为参数的逻辑4 当不为参数时,检测当前url是否和规则相等