ThinkPHP路由源码解析二

路由是项目开发中比较重要的一个环节,每个项目都会使用路由进行管理接口,接下来本文会从源码方面带大家一起学习路由。

前言

由于文章篇幅的原因,执行在新开一篇文章进行写。

在上一篇中给大家讲解了以下内容。

  • 路由初识化简单分析
  • 通过定义路由再谈门面
  • 路由定义rule方法中的$this->group到底执行了什么
  • 路由规则预处理
  • 解析生成路由标识的快捷访问

但是在路由这块还有很多的内容要来讲解,接下来就会针对以下内容进行解析。

  • 路由参数
  • 变量规则
  • 资源路由
  • 路由配置(就是在route文件中的return中)
  • dispatch初认识
  • route-check 检测URL路由
  • 。。。。。。。。。。。。。

接下来就一个一个进行详解。

同样给大家放一个关于路由的执行图,供大家进行参考。

路由的执行流程

一、路由参数和变量规则

同样代码先从这里开始,但是这次的关注点是在路由参数和变量规则上。

这俩个点只是简单的给大家说明一下使用和简单的执行流程。

比较深的理解就不去说明了, 因为这俩个点在平时开发过程中是不怎么使用的。

注册路由规则

在上文中还记得在文件thinkphp/library/think/route/RuleItem.php中我们看到过关于路由参数的使用吧!

关于路由参数处理的地方

并且文档中也给了很多支持的参数,如下图。

文档支持的参数

那这个路由参数是怎么使用的呢!

接着使用之前我们使用的路由,并且给设置上URL后缀检测,支持匹配多个后缀。

路由案例

那么我们设置的这个路由应该怎么访问呢!

如果按照正常的路由规则进行访问是会报错的,正确的访问方式请继续看。

访问结果

正确的请求地址应该为http://www.source.com/index.php/hello/1.html,也就是在请求地址上需要拼接我们设置的后缀html。

访问结果

这块的具体执行流程使用代码追踪器简单的看一下即可,咔咔这里就不做演示了。

对于路由参数咔咔这里在给大家演示一个案例,就基本结束了。

全局路由参数

路由参数这块最后一个说明的就是全局路由参数。

直接来到文档就可以看到。

全局路由参数

这里咔咔设置俩个路由规则做测试,一个参数可选,一个必选,并且设置上路由规则添加文件后缀。

路由文件

传参数时的请求地址。

访问结果

不传参数的请求地址。

访问结果

以上就是针对路由参数写的内容。

没有去做特别深的讲解,基本就是说明了怎么使用,有这个东西的存在,所以了解即可。

变量规则

同样的变量规则,这个在咔咔的日常开发工作中是更不可用的。

变量规则咔咔认为唯一的好处就是对参数进行过滤。

也就是说变量规则是在路由规则存在参数时才会存在的行为。

咔咔这里给大家提供一个简单的案例即可。

演示的案例

路由文件演示的案例

在路由后边追加参数pattern

那么接下来看一下这个请求如何写。

当参数为数字时可以输出传入的参数。

第一次请求地址

但是当传入字母的时候就会报错。

打印结果

所以说变量规则就是对路由规则后的参数进行的过滤,也就是使用正则进行处理的。

截止到这里关于路由参数和变量规则就简单的说到这里了。

虽然说内容很简单,大多数都是使用案例给大家介绍怎么使用,没有去对源码进行深究。

第一平时开发不怎么会去使用。

第二在后文中还需要进一步深入了解,这里只是做一个认识。

其实说到这里估计还是有很多人不明白,为什么输入路由地址就可以出现对内的内容,这个后期会进行深入的了解。

二、资源路由

资源路由的设置也是很简单。

路由文件

同时使用命令行来创建文件也更是方便。

使用命令进行创建文件

创建的控制器本身就是一个资源路由文件。

资源路由控制器

接着还是会执行到Route文件的resource方法。

这个方法同样会有路由规则、路由地址、路由参数、变量规则。

注册资源路由

接着就会来到thinkphp/library/think/route/Resource.php

在这个类中及时做一些简单的属性赋值。

再就是咔咔圈出来的地方,接下来咔咔会对圈出来的地方进行说明。

thinkphp/library/think/route/Resource.php

由于resource类继承这RuleGroup类,所以会跳转到thinkphp/library/think/route/RuleGroup.php类里。

来到这个方法执行的动作。

  • 会对路由规则进行简单的处理,如果路由存在参数则需要把路由规则转换为blog/<name> 或者 blog/<name? >
  • 接着就是对$this->parent解释,这里为什么是object(think\route\Domain),需要需要路由文章的第一篇第三节,并且在这一步就会对fullName进行赋值。
  • 最后一步就是进行对fullName进行赋值,其实就是将路由规则赋值给fullName。

设置分组的路由规则

然后在就返回上层看接下来的东西。

返回上层文件继续阅读源码

设置分组的路由规则处理完之后就是对一些属性进行赋值,主要为一下三个。

  • 路由变量规则
  • 路由参数
  • REST路由方法定义

属性赋值就没有什么说的了,就是需要明白对应的属性做的事情即可。

在紧接着就是会以请求方式为键值 以$this为值 返回本类实例,并且会进行检测此路由方法是否为注解路由。

返回的结果咔咔打印出来给大家简单的看一下,内容没有截取全,知道这个值是什么就行了。

返回结果

以上执行完之后就会返回上层去执行lazy方法

首先知道这个传入的这个参数是什么:路由是否延迟解析

执行完返回上层的代码

接着就会来到lazy方法

lazy方法

在这个方法中会去调用parseGroupRule,并且传入的参数就是分组的路由规则

这块的内容就是关于路由分组的,这里就不走探讨了。

分组的路由规则

直到这里关于资源路由就简单说到这里

最后咔咔将执行脑图给大家画出来

资源理由执行流程

三、关于域名是什么时候设置的

其实在上节中存在一行这样的代码$this->parent->getDomain()

这行代码根据方法名字都知道是获取请求域名的

但是都知道这个域名是在哪里进行设置的吗?

当你请求这个资源路由时,就会执行到Route类的构造函数

资源路由

在构造函数里边有这样一行代码,就是来获取请求域名的

构造函数

就会来到host方法

在host方法中,这个参数为true

这里只需要关注$this->server('HTTP_HOST')即可,就是用来获取域名地址的

在咔咔圈出来的第二处可以看到,第一个条件是成立的,但是第二个条件是不成立的

所以会直接返回上一步获取出来的$this->host

host方法

打印$this->host,这里为什么会执行俩次就不对此解释了,有疑问的可以使用函数debug_backtrace来进行查看。

打印结果

紧接着就会执行初始化默认域名这个方法

也就是在这里对域名进行了初始化

初始化默认域名方法

此时还会有疑问就是明明是在thinkphp/library/think/route/Resource.php这个类里边执行的啊!

设置域名地址是在thinkphp/library/think/Route.php这个类里边的啊!

关于这个一想就能明白是因为继承关系的啦!

thinkphp/library/think/route/Resource.php

那么到这里对于这个域名的设置应该就很清楚了,如果还不明白就多看看咔咔写的文章哈!

咔咔写一篇文章估计需要几个小时,但是你们看也就几分钟,几分钟能看个啥啊!

对着代码慢慢,要的是质量,不是效率哈!

那么截止到这里关于域名是在什么时候设置的就讲解完了,接下来会对路由配置-数组方式配置解读

四、路由配置-数组方式配置解读

其实这里估计很多人看了后都会有点懵。

大多数都是不理解,这是什么意思,这玩意在哪!

其实这个配置是在5.1之后才有的,咔咔翻过之前的版本是不存在这个配置的。

这个配置的位置其实就是咱们一直学习的Route路由里边,没想到吧!就是这么强大。

就是路由配置里边的那个return。

路由配置文件

在回顾一下路由文件的加载吧!

首先是入口文件,这个是毫无疑问的。

咔咔圈起来的地方会返回App的这个实例,如果有不明白的可以去看容器那一篇文章。

所以这行代码会执行到App类的run方法。

入口文件

那么来到thinkphp/library/think/App.php这个类的run方法看一下。

能直接看到的就是初始化应用。

初始化应用

来到初始化应用就可以看到关于路由初始化

路由初始化

来到routeInit方法

在导入路由配置时是对其有过一次判断,这里的判断就是针对的路由配置文件中的数组方式配置。

导入路由配置

到这里关于路由配置什么时候加载的应该就有一定的了解了。

但是关于数组方式配置路由在文档中是没有提到的,也就是说并没有使用案例。

所以我们就需要在根据import这个方法进行追踪。

直接找到使用方法即可。

这里关于$this->route->import($rules);这行代码,咔咔在做一次解释。

其实也就是$this->route为什么可以执行到Route类。

首先我们需要先知道的就是App类是继承这Container类的

继承关系

然后在Container类的最后存在几个魔术方法。

Container魔术方法

当App中调用不存在的Route类时就会执行这个__get这个魔术方法。

然后就会执行__get魔术方法中的make方法。

关于这个方法,咔咔说过了不下三次了,可以去看容器那一篇文章哈!

容器类中的make方法

接着继续我们的行军之路,来到thinkphp/library/think/Route.php的导入配置文件的路由规则,方法import

在这个方法中可以看到几个熟悉的变量规则

那么使用这几个值进行简答的测试一下

检测导入配置文件的路由规则

刚好在第二节中对资源路由进行了说明,那么接下来就使用检查资源路由为条件进行测试

一定要把之前设置的资源路由配置给关闭,否则是无法进行测试的

路由配置文件

这个时候进行一次访问

没错,就是这样,使用起来就是如此的简单

访问结果

其实数组方法配置路由就跟资源路由是一样的,只不过是执行的顺序而已

最终也是通过循环将路由规则传递给resource方法

执行resource

同样也是执行的这个方法

resource方法

本节就简单的聊到这里,主要就是对路由初始化需要有一定的理解

并且学会使用路由数组方式的配置

这种方式不论是在之前的版本还是5.1还是发布的6.0版本都是不经常使用的,也可以说是根本不会用到的

这里提到只是为了给大家在温习一下路由初始化的过程,和知道数组配置路由到底是干什么的

哪里有疑问就要解决哪里,而不是放着不去解决

五、dispatch初认识以及route-check 检测URL路由

这块的内容是在执行应用程序里边,接下来咔咔带大家简单的认识一下。

本节没有源码的解释,只是为了后文坐铺垫使用的,所以很有必要知道dispatch是怎么一个回事。

下图就是在执行完路由初始化之后返回上层继续执行的流程。

然后就会执行到路由检测这里。

dispatch初始执行位置

路由测试使用如下图

路由测试使用案例

然后我们可以对这个调度信息进行打印

打印数据

打印结果

在上图中已经打印出了关于dispatch的相关的值

接下来就会针对routeCheck方法进行简单的预览

routeCheck

在上图方法中只要明确在这一步会处理缓存,并且返回一个Dispatch对象即可。

这块的源码大家可以简单的看一下即可,不是很重要。

route-check 检测URL路由

但是这块的内容还是需要简单的去看一下的。

在看之前需要明确一下传入的俩个参数分别是什么。

参数一:路由规则
参数二:检测是否配置了强制路由

返回一个dispatch对象

知道了参数的含义后就需要去到check这个方法中一探究竟了。

检测url路由

在这个方法中关于自动检测域名路由我们来打印一下数据是什么样的。

其实这个返回的结果就是跟之前资源路由挂载的方式是一样的,

打印结果

然后会通过pathinfo分隔符 : 把url中的 / 改为 |

并且会在配置文件获取路由是否完全匹配

最终执行使用默认路由解析

这里边的细节就不去深度解析了,关于路由这块的细节是在是太多了,如果一个一个去针对细节,那是需要耗费大量的时间的。

所以这块的内容就到这里了,只需要知道执行了什么,并且最终返回的是什么即可。

六、request类是如何找到的

在上一节中$result = $domain->check($this->request,$url,$completeMatch);会执行这一块的内容。

这里不去关心这个方法执行了什么。

而是需要关心这个$this->request是如果找到并且执行的。

首先可以看到的是在Route类中是存在request这个属性的。

请求对象的属性

接着来到Route的构造函数,在这里你会发现新天地。

此处使用了ArrayAccess像数组一样访问对象,但是$app中不存在request属性,所以就会去执行容器类中的__get魔术方法,在__get方法中调用的是容器中的make方法,第一个参数为request,最终会返回request的实例。

构造函数

这里的$app其实就是通过依赖注入进来的App实例。

看了这么多的源码肯定知道App类是继承Container类的也就是容器类。

在容器类中得最下边会有几个魔术方法。

这里只需要关注__get方法即可。

魔术方法

__get方法会在访问不存在的属性时会执行的函数。

也就说最终会执行到make方法。

容器类中的make方法

这个方法会通过一系列的操作,最终返回一个Request的实例。

并且把这个实例存放到容器里边,下次使用时直接获取即可。

关于容器类中的make方法是容器类中特别重要的方法,也是灵魂方法。

全框架的实例都是通过容器返回的,所以说这个方法的重要性就不用咔咔在多说了。

咔咔之前对容器进行过特别深入的理解,并且用文章的形式呈现给了大家。

八、检测域名路由

先给大家把流程图画出来,然后根据流程跟这咔咔的节奏即可。

路由域名检测流程图

首先要确认的一件事情就是检测域名路由是在执行应用程序中执行的。

上层执行流程就是在入口文件哪里。

执行应用程序

首先代码会执行到routeCheck这个方法里边,那么就先看这个文件。

先看注释,对这个方法的解释就是URL路由检测。

在这个方法里边先是会对路由缓存进行检测,这块内容就是关于Cache的。

在这个方法里边最重要的的就是路由检测 返回一个Dispatch对象就是这个方法。

URL路由检测

那么接下里就是看这个方法。

首先要明确的就是传进去的俩个参数都是什么。

  • $path : string(4) “blog”
  • $must : bool(false)

路由检测

在检测URL路由中会做以下几件事情。

  • pathinfo分隔符 : 把url中的 / 改为 |
  • 路由是否完全匹配
  • 检测域名路由
  • 默认路由解析

接下来就只需要对检测域名路由流程进行深度解析。

关于前俩个执行只是一些字符串的处理,看看就行,知道最终返回什么即可。

检测URL路由

同样在检测域名路由的执行中明确三个参数的含义。

  • $this->request : 通过容器类的__get魔术方法,执行容器类的make方法,最终返回request的实例对象,这列不会的去看第六节的文章
  • $url : string(4) “blog”
  • $completeMatch : 路由是否完全匹配

来到$result = $domain->check($this->request,$completeMatch);这里,也就是本节的重点了。

在这个方法里边会执行以下几个流程,会针对重要的执行流程进行深度解析。

  • 检测路由别名 : checkRouteAlias
  • 检测URL绑定:checkUrlBind
  • 判断路由参数
  • 添加域名中间件
  • 检测分组路由 : parent::check

检测域名路由

检测路由别名 : checkRouteAlias

参数解释

  • $request : request类的实例
  • $url : 传过来的 blog

在这个方法里边存在俩个需要明确的知识点

  • strpos : 查找在字符串中第一次出现的位置
  • strstr : strstr返回一个指针,指向string2在string1中首次出现的位置,strstr(“Helloworld!”,“world”);?>\n输出:\nworld!
  • 首先会对URL地址进行处理:返回blog
  • 获取别名路由定义 NULL
  • 以资源路由blog为例 返回false

检测路由别名

在检测路由别名中存在一个方法需要去在看一下

参数就是上图中传入的blog

获取别名路由定义

来到这个方法,首先要明确的事情就是此方法在类thinkphp/library/think/Route.php

并且此类使用了think\route下的所有类

这个方法就会把从检测路由过来的blog然后会在Route类中的alias属性里边进行获取,如果不存在则会返回NULL

这个别名的使用会在下文中提到

获取别名路由定义

来到检测别名路由的最后return $item ? $item->check($request,$url) : false;也就是这行代码,从上图中就可以知道,这个item就是NULL

并且最终将这个NULL给返回回去。

检测URL绑定:checkUrlBind

参数说明

  • $request : request类的实例
  • $url : 传过来的 blog

在这个方法中只对下图咔咔圈出来的地方进行详解。

检测URL绑定

来到方法getBind读取路由绑定,可以看到咔咔已经将传入的参数打印出来了。

本方法是在thinkphp/library/think/route/Domain.php这个类里边,还记得在设置路由规则的$This->group就是使用的这个类,不知道的可以去看路由文章的第一节。

同时在这个方法中会进行一次subDomain当前子域名的获取。

在这个方法最终会返回www,主要看一下圈出来的第一个部分。

通过request类中的host方法来获取当前域名,然后进行分割。

返回数据:array(1) { [0] =>\n string(3) “www”\n}

给子域名赋值:$this->subDomain

返回最终结果返回子域名 : www

获取当前子域名

接着就会返回到上层,在上层进行判断获取的当前子域名WWW。

一些是所有的判断处理,第一个判断肯定是不会成立的,因为只返回了www,并没有.

下边的判断是根据路由绑定进行的判断,这里只需要知道最总会返回NULL就可以了。

进行的判断

知道了在底层返回了NULL,所以在这里的判断同样也不会成立,所以最终给上层返回的结果就是false。

检测URL绑定

判断路由参数

根据上图执行流程最终还是会返回到thinkphp/library/think/route/Domain.php这个方法check检测域名路由。

然后开始进行判断路由参数。

没有路由参数跳过不执行。

存在路由参数:执行方法setRouteVars :设置路由变量 这个参数是在框架版本5.1.5以上才可以使用,由于咔咔使用的版本有点低,就不对其详解了。

检测域名路由

文档

添加域名中间件

关于中间件这里也不对其进行解释,因为后期会新开一篇文章来详解,本文还是以路由为重点哈!

检测分组路由

接着就会来到检测域名路由的最后一个流程,执行代码return parent::check($request,$completeMatch);

会跳转到类文件:thinkphp/library/think/route/RuleGroup.php,因为Domain类是继承RuleGroup这个类的。

参数说明

  • $request : request类的实例
  • $url : 传过来的 blog
  • $completeMatch : 路由是否完全匹配

在这个方法中咔咔只会对这里的其中的一个流程进行详解,也就是合并分组参数。

因为这个方法也是贯穿执行流程的一条主线,其余的都是方法都是在进行检测,判断。

合并分组参数

九、总结

关于路由用了俩篇文章还没有结束,看了这么长时间的源码也就是路由这块是最复杂并且最难理解的。

其中的类是一环套一环,路由先暂时了解到这里,后期在阅读其它源码时在进行其它内容补充。

在路由这一篇中主要执行的流程图大家一定要仔细看。

执行流程图

在就是通过在注册路由规则时的group这个属性最终返回的是Domain类,这里的内容一定要清晰。

主要知道在路由中域名的配置流程,域名是在何时进行配置的。

路由文件中的返回数组和在导入路由文件流程要有清晰的思路。

再就是回顾之前学习的ArrayAccess,像访问数组一样访问对象。

容器中的魔术方法__get方法,在这个魔术方法中存在make方法,主要用来返回一个类的实例,并且存放到容器中。

关于路由的方面暂时就说到这里的,预计在有一篇就会把路由写完了。

坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在诺大互联网中咔咔的文章能带给你一丝丝帮助。我是咔咔,下期见。

相关文章

(1)创建数据表: CREATE TABLE IF NOT EXISTS `think_form` ...
组合查询的主体还是采用数组方式查询,只是加入了一些特殊的...
(1)创建模版:/App/Home/View/Form/edit.html   <FORM m...
自定义配置文件user.php: <?php return array(    \'se...
在一些成熟的CMS系统中,后台一般都包含一个配置中心(如织梦...
废话不多说先上图预览下,即本博客的分页; 这个分页类是在...