# 执行流程
上一节大家已经了解了路由的基本概念和简单用法,本篇就来详细了解下路由的工作原理和执行流程。
路由的执行过程主要分为三大部分:路由注册、路由检查和路由解析,而每个部分又包含很多步骤。
## 注册路由
要使用路由功能,必须确保开启路由功能:
~~~
// 开启路由功能
'url_route_on' => true,
~~~
>[danger]### 【5.1须知】
> * * * * *
> 不需要配置开启路由,默认开启且不能关闭
然后进行路由规则的注册(或者说定义),路由的注册包括单个路由、路由分组、路由别名、资源路由、变量规则注册以及域名路由注册等等,对于开发者来说,路由的主要工作就是注册路由(上面提及的注册环节我们在后面的内容中都会陆续讲解),而路由的匹配和解析一般是由系统自动完成的。
注册路由的时候会把路由规则注册到对应的请求类型(包括`GET`/`POST`/`PUT`/`DELETE`/`PATCH`/`HEAD`/`OPTIONS`)数组,如果是`any`或者`rule`方法直接注册的(也包括批量导入的)则会注册到一个默认的`*`请求类型(代表全部请求类型有效),但同时也会分别在各个请求类型的路由规则里面注册一个快捷方式(使用`true`注册),告诉系统当前请求类型的该路由规则请直接获取`*`中的路由规则定义。
### 路由配置
如果在应用配置中关闭了路由功能的话
~~~
// 关闭路由功能
'url_route_on' => false,
~~~
则不会进行路由的注册和检测,前面我们提过不要在路由配置文件之外进行路由注册,因为如果你是在应用的公共函数文件中注册了路由,即使你关闭路由也仍然会去注册路由,虽然没有任何意义。最关键的一点是不在路由配置文件中注册的路由无法进行路由缓存。
5.0的路由配置是**不支持在模块配置文件中定义**的,因为在读取模块之前,路由已经检测完成了。如果你想做的是给不同模块注册的路由规则能够分开不同的文件,便于开发的时候分工协作,那其实是有办法的,只需要修改应用配置文件中的下面参数:
6.
~~~
// 设置路由配置文件列表
'route_config_file' => ['home','admin'],
~~~
`application/home.php`配置`home`模块的路由规则,`application/admin.php`则配置`admin`模块的路由规则。
虽然运行的时候依然会同时加载并注册,但定义的时候是明确分开了,便于协作。
> 我们又GET了一个新技能:原来路由配置文件不一定是`route.php`。
>[danger]### 【5.1须知】
> * * * * *
> 直接可以在`route`目录下面添加不同模块的路由定义文件,无需配置
>
### 注册方法
完成路由注册功能的方法有很多,包括:
|方法名|描述|
|---|---|
|rule|基本路由注册|
|any|任意请求路由注册|
|get|GET请求路由注册|
|post|POST请求路由注册|
|put|PUT请求路由注册|
|patch|PATCH请求路由注册|
|delete|DELETE请求路由注册|
|alias|别名路由注册|
|group|路由分组注册|
|controller|控制器方法路由注册|
|resource|资源路由注册|
|pattern|全局路由变量规则注册|
|import|静态注册路由(导入路由配置)|
|domain|域名路由注册或者域名绑定|
|miss|MISS路由注册|
|auto|AUTO路由注册|
这些路由注册方法中最基本的就是`rule`方法,其它大部分注册方法都是调用该方法。
路由注册其实有一些比较特殊的情况,比如分组路由就是先注册一个路由分组,然后在分组下面再注册实际的路由规则,同时这些路由规则可以公用所属路由分组的参数及变量规则,免得重复定义。关于路由分组和一些特殊路由的用法,我们会在后面单独讲解,暂且不必深究。
路由注册后,你可以通过下面的方法查看所有的路由规则(包括路由变量、域名和别名等信息):
~~~
// 获取全部的路由信息
Route::rules();
// 获取GET路由信息
Route::rules('GET');
~~~
>[danger]### 【5.1须知】
> * * * * *
> 5.1路由的存储方式改为对象存储,所以`rules`方法作用已经改变
>
### 路由缓存
路由注册是在每次请求的时候完成,然后再进行路由检查。那么问题来了,为啥每次都要重复注册路由规则呢,能否把路由规则缓存起来?
答案是当然可以,在开发阶段由于会进行路由的调整,当实际部署上线的时候,其实路由规则就不会变化了,那么我们就可以把路由规则缓存起来,以后的每次请求就不需要重复执行路由注册操作了,能够提高不少的性能(尤其你的路由规则相当多的情况下)。
缓存路由很简单,到操作系统的命令行下面,在应用根目录下面执行命令:
~~~
php think optimize:route
~~~
执行后会在`runtime`目录下面生成一个`route.php`路由缓存文件,再次请求的时候就会直接读取缓存文件里面的路由规则。
注册和读取路由规则的关键代码在 `think\App`类的`routeCheck`方法,主要代码如下:
~~~
if (is_file(RUNTIME_PATH . 'route.php')) {
// 读取路由缓存
$rules = include RUNTIME_PATH . 'route.php';
if (is_array($rules)) {
Route::rules($rules);
}
} else {
$files = $config['route_config_file'];
foreach ($files as $file) {
if (is_file(CONF_PATH . $file . CONF_EXT)) {
// 导入路由配置
$rules = include CONF_PATH . $file . CONF_EXT;
if (is_array($rules)) {
Route::import($rules);
}
}
}
}
~~~
路由缓存只会缓存在路由配置文件(确切的说是`route_config_file`配置的文件列表)中定义的路由规则(包括动态注册和静态注册的规则),所以再次强调,不要在路由配置文件之外注册路由规则,否则将享受不到路由缓存带来的性能提升。
>[danger]### 【5.1须知】
> * * * * *
> 5.1的路由注册支持延迟注册,性能大幅提升,因此不需要路由缓存,该指令的作用变化,如果需要使用路由反解的话 可以执行该指令生成缓存。
>
## 路由检查
路由检查指的是把当前的请求URL地址依次和注册的路由规则进行变量和条件检查,如果不匹配则进行下一个路由规则的检查,直到匹配到正确的路由规则则进入下一步:路由解析。如果没有匹配到任何路由规则,则按照系统默认的规则进行URL解析(如果有定义MISS或者AUTO路由则接管,这两个路由定义会在路由高级里面讲解)。
路由检测的路由规则是当前请求类型的路由规则,其它请求类型的路由就没必要浪费时间一起检查了,下面的代码就是获取当前请求类型的路由规则。
~~~
$method = $request->method();
// 获取当前请求类型的路由规则
$rules = self::$rules[$method];
~~~
一个特殊的情况是,使用`any`方法(以及不带第三个参数的`rule`方法)动态注册或者静态注册的路由规则这个时候是获取不到实际的路由规则,只是会有一个`true`的规则存在,当实际检测的时候才会去获取真正的规则。
路由匹配的过程是路由执行流程中最复杂的部分,包括很多的检查环节,除了关键的路由参数检测和变量规则检测外,还涉及到别名检查、域名部署检测、路由绑定检测、静态路由检测等等,如果你希望分析路由检测的代码的话从`\think\Route`类的`check`方法开始着手分析即可。
前面我们已经知道了路由注册的基本方法如下:
>[info]#### Route::rule('路由规则','路由地址','请求类型','路由参数(数组)','变量规则(数组)');
我们先来了解下关键的路由参数和变量规则检测:
### 路由参数检查
路由参数检查主要是完成路由请求的有效性进行检查(路由的请求类型其实也是一个重要的路由参数,只是已经提前过滤了),路由参数检测的方法是`Route`类的`checkOption`方法。
路由参数定义采用数组方式,用于路由检查的参数包括:
| 参数 | 说明 |
| --- | --- |
| method | 请求类型检测,支持多个请求类型 |
| ext | URL后缀检测,支持匹配多个后缀 |
| deny_ext | URL禁止后缀检测,支持匹配多个后缀 |
| https | 检测是否https请求 |
| domain | 域名检测 |
| before_behavior | 前置行为(检测) |
| callback | 自定义检测方法 |
|ajax|Ajax检测(`V5.0.2+`)|
|pjax|Pjax检测(`V5.0.2+`)|
> 这些路由参数可以混合使用,只要有任何一条参数检查不通过,当前路由就不会生效,继续检测后面的路由规则。
路由参数的定义比较简单,从字面意思就能理解,注意的是`ext`和`deny_ext` 如果要支持多个后缀的话,使用`|`分割即可。最后的两个自定义检测我们会在路由高级部分给你讲解。
>[danger]### 【5.1须知】
> * * * * *
> `5.1`推荐使用对应方法替代参数方式定义
>
### 路由变量检查
路由变量检查,其实也就是通常说的路由规则匹配检查,当路由参数(路由生效条件)检测通过后,就要对当前访问的URL地址进行路由规则匹配检查,包括路由变量个数、变量规则约束,如果是静态路由规则的话就进行字符串匹配(不区分大小写)。
路由的变量规则有不同的生效范围,可以在单个路由规则里面定义(局部变量规则),也可以使用`pattern`方法进行全局变量规则定义。
~~~
// 全局变量规则
Route::pattern(['id'=>'\d+','name'=>'\w+']);
// 局部变量规则
Route::get('hello/:name','index/index/hello',['ext'=>'html'],['name'=>'\w+']);
~~~
详细的路由变量定义和使用,我们会在下一节为你讲解。
>[danger]### 【5.1须知】
> * * * * *
> 参数写法可以使用方法用法,更加清晰:
> ~~~
> Route::get('hello/:name','index/index/hello')
> ->ext('html')
> ->pattern(['name'=>'\w+']);
> ~~~
在进行路由匹配检查之前,其实还会依次进行下面的一些额外检测:
### 路由别名检查
路由别名允许我们给控制器注册一个唯一的路由标识,然后该控制器下面的所有操作方法都不需要再定义具体的路由,这个路由标识就称为别名路由,详细的别名路由的用法我会在[(六)别名路由](223114)为你讲解。
### 域名部署检测
接下来会检查是否配置了域名部署,如果有匹配当前的域名,会检查域名是否定义相关的路由绑定。
域名部署检测由`Route`类的`checkDomain`方法完成,具体用法我们会在[(十)域名路由](223118)一节为你讲解。
### 路由绑定检查
在这个步骤,系统会检查当前的是否有进行路由绑定,如果有绑定的话按照绑定类型进行解析。具体内容会在[(十一)路由绑定](223119)中为你讲解。
### 静态路由检查
系统会优先检查是否存在和当前访问URL地址相同的静态路由(注意是完整匹配,不含URL后缀),如果有定义,则进行静态路由的参数检查,如果通过表示路由有效,则进行路由地址解析。
也就是说静态路由规则虽然和动态路由是使用相同的方式一起注册的,但检查的时候是提前检查的。
## 路由解析
路由解析的主要工作就是解析匹配到的路由规则中定义的路由地址(例如控制器的操作方法或者闭包等),并且解析URL地址中的其它路由参数以及路由绑定的其它数据,而且会把相关信息和变量保存到当前请求对象中,最后会告诉系统下一步如何对URL请求进行正确的调度执行,这个时候路由的使命全部完成,正式交权给`App`类。
路由的解析操作由`Route`类的`parseRule`方法完成。
## 小结
现在你已经基本了解了路由的执行流程,以及进行路由检查的相关步骤,一下子体验了很多环节,可能会有一些困惑,不过不要紧,下一步我会从路由变量的使用细节开始讲起,路由之旅正式启程啦。