今天啃关于路由解析的部分,感觉这块还是挺复杂的;有的点还是没看透,把看明白的总结出来。
路由解析的流程
我们在使用路由解析的时候,很多部分参与了路由解析,远不止tp框架如下图
可以看出从客户端发起到服务器处理响应,经理的4个阶段,tp框架只是其中一部分。
路由的意义
url作为一种输入的数据,过路由解析,
匹配到应用业务控制器(也有可能是闭包函数和自定义的类)
路由相关的参数~~~~
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 当引入
会执行其中标红的部分,返回的数组,会执行Route::import 方法,而在注册路由的过程中除了分组路由以外(我们另外分析),最后都是对Route::setRule的封装,所以主要分析一下setRule
首先介绍一下Route的$rules的结构
rule 为路由表达式
route为匹配路由路径
var 为指定参数 其中key为参数名 value的值 有1 或2 其中1 是必须添加 2为选填
option 为的值路由参数中提及的值(不明白请查阅文档)
pattern 为次路径下的参数限制
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导入配置文件中的批量配置
路由解析
最后在解析完成后 Route::parseRule 会返回一个result数组,为路由的调用提供依据。
2 检测路由别名
别名的设置如下
1 检测是否为变量
2 检测是否为可选参数,如果是,去掉[]并将代表是否为可选参数的变量置为响应的值
3 执行为参数的逻辑
4 当不为参数时,检测当前url是否和规则相等