# 執行流程
上一節大家已經了解了路由的基本概念和簡單用法,本篇就來詳細了解下路由的工作原理和執行流程。
路由的執行過程主要分為三大部分:路由注冊、路由檢查和路由解析,而每個部分又包含很多步驟。
## 注冊路由
要使用路由功能,必須確保開啟路由功能:
~~~
// 開啟路由功能
'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`方法完成。
## 小結
現在你已經基本了解了路由的執行流程,以及進行路由檢查的相關步驟,一下子體驗了很多環節,可能會有一些困惑,不過不要緊,下一步我會從路由變量的使用細節開始講起,路由之旅正式啟程啦。