thinkphp5 源码分析之二 路由解析

今天啃关于路由解析的部分,感受这块仍是挺复杂的;有的点仍是没看透,把看明白的总结出来。php

路由解析的流程

咱们在使用路由解析的时候,不少部分参与了路由解析,远不止tp框架以下图
image.png
能够看出从客户端发起到服务器处理响应,经理的4个阶段,tp框架只是其中一部分。html

路由的意义

url做为一种输入的数据,过路由解析,
匹配到应用业务控制器(也有多是闭包函数和自定义的类)thinkphp

路由相关的参数~~~~

image.png
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官方手册毕竟这里咱们是以分析源码为主。
image.png服务器

源码解析

我将tp5的路由解析是将咱们配置文件中配置的路由规则注册进thinkRoute.php的私有静态变量$rules,本质上来讲检验路由就是按规则将$rules中的信息进行校验, 因此为了便于理解,我将讲述分为两部分, 一部分是路由注册, 一部分是路由解析;下面将会根据这两部分进行分析。闭包

路由注册

整个过程以下图
image.png
入口
app::run==>app::routeCheck===>route::import
在routeCheck中会检查是否开启路由缓存,同时会去RUNTIME_PATH下查找是否有缓存的路由文件,若是没有引入文入路由文件,在本例中便是route .php 当引入app

image.png

会执行其中标红的部分,返回的数组,会执行Route::import 方法,而在注册路由的过程当中除了分组路由之外(咱们另外分析),最后都是对Route::setRule的封装,因此主要分析一下setRule
首先介绍一下Route的$rules的结构
image.png
其中get到options位置都是类似的,一个整的注册相似与
image.png框架

rule 为路由表达式
route为匹配路由路径
var 为指定参数   其中key为参数名  value的值 有1 或2  其中1 是必须添加 2为选填
option 为的值路由参数中说起的值(不明白请查阅文档)
pattern 为次路径下的参数限制

另外请注意,若是在注册路由时,指定的方式传类型
image.png
会有一个优化(在我理解)
image.png
会将路由直接对应一个true,而其对应的参数在$rules中的*中存储
image.pngdom

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导入配置文件中的批量配置

路由解析

image.png
路由检测主要集中在check方法内 主要过程是
image.png
静态检测
image.png
路由规则检测
image.png
也就是一下6部分
1 检查路由缓存
若开启 route_check_cache 时,则在第一次缓存后将匹配成功的路由参数
存储在缓存中
image.png
直接调用Route::parseRule进行解析
其实在check的最后检查过5项后最后第六步也会调用parserule进行路由转换,这里就一并分析了
简单分析下parserule关键的地方
image.png
(1)闭包注册
判断是否为闭包类 若是是result返回闭包对象
(2)若是路由是到重定向地址
tp5路由规则中 首字符为/ 或包含http://既为重定向地址
若是是则返回result 包含当前的路由地址,若是路由没有设置status参数则默认为301
(3)路由到方法
tp5 的路由到方法的格式为
image.png
路由地址必须从跟命名空间开始,因此开头必须为'\'
这里处理将$route 中的@替换回来
提取@后的method
最后返回result
(4)路由到控制器操做
image.png
当首字符为@的时候则定义到控制器方法,
这里除了解析字符串返回$result对象意外
会调用Request->Action执行对应的方法
(5)路由到普通的 既模块/控制器/操做
image.png
最后在解析完成后 Route::parseRule 会返回一个result数组,为路由的调用提供依据。

2 检测路由别名
别名的设置以下
image.png
咱们能够看到 若是别名对应存储的不是一个字符串地址,而是参数数组,则会依次检查参数的是否符合
image.png
后面依次是参数有效性检查 ; 检测别名对应地址,匹配其路由到类,路由到控制器,路由到模块/控制器,的需求
image.png
最后若是匹配成功,返回result数组,若是匹配失败则返回false,执行其他步骤
3 检测域名部署
这里也将域名的注册 和域名的检查同时介绍
域名注册:
域名能够绑定的类型有三种
image.png
下图为注册函数
image.png
能够看见domain函数会直接进行注册,将路由地址,路由参数,参数规则
image.png
这里我以为闭包函数的规则有点鸡肋
1 执行时机
image.png
竟然是在执行注册的时候就进行调用,我不明白这样的调用有什么意义
2 另外只执行了调用却没有对闭包的任何参数进行注册。
闭包存在的意义就只有分别对各自域名进行注册
image.png
但是因为这个闭包注册是定义在配置文件中的,那样就是说不论我是访问哪个子域名,我仍是要加载全部的闭包注册。因为这些缘由因此感受仍是比较鸡肋的。
而动态注册规则
最终会执行
image.png
将参数的规则除了在name中
另外注册一份在domain中,结构以下图
image.png
域名检测
对域名进行解析
image.png
得到当前路由规则中对应的值
若是对应的规则包含'['bind']' 说明不是动态注册规则,则在解析后,将路由规则注册进$bind私有成员变量
image.png
若是不包含说明是动态注册规则
image.png
则替换目前的规则
image.png
4 检测URL绑定
image.png
这里就是处理上一步中域名绑定的规则,将其处理为result返回跳转
5 静态路由规则检测
image.png
静态路由是指,访问连接和注册连接一致
image.png
6 路由规则检测
路由规则检测最后部分是调用的是Route::parseRule,这一部分在1中已经分析过,在这里就不重复了,这里只分析Route::checkRoute层面上作了什么
1 遍历当前访问方式的rules
image.png
当前访问方式的规则数组rules,遍历当前访问方式的路由规则
image.png
若是是分组路由则递归调用自身,并将分组参数传入
image.png
也就是说咱们注册时填充的路由分组,到这里会统一将分组名称填写彻底后进行路由检测
image.png
执行前置的参数检查,最后在C:wamp64wwwthinkphplibrarythinkRoute::match中执行路由的参数,和当前url 是否符合路由的检测
image.png
检测$m2 为当前路由规则
$m1 为当前url
image.png
从注册中能够看出路由规则 和咱们当前的访问url 除开参数意外,应该是一一对应的,代码中为
image.png1 检测是否为变量2 检测是否为可选参数,若是是,去掉[]并将表明是否为可选参数的变量置为响应的值3 执行为参数的逻辑4 当不为参数时,检测当前url是否和规则相等

相关文章
相关标签/搜索