ThinkPHP路由源码解析(一)
路由是項目開發中比較重要的一個環節,每個項目都會使用路由進行管理接口,接下來本文會從源碼方面帶大家一起學習路由。
框架路由解析
- 前言
- 一、路由初識化簡單分析
- 二、通過定義路由再談門面
- 三、路由定義rule方法中的$this->group到底執行了什么
- 四、路由規則預處理
- 五、解析生成路由標識的快捷訪問
- 六、總結
前言
使用框架寫過項目的肯定都使用過路由,使用路由來進行接口的管理,那么為什么要使用路由呢!
使用路由會保護項目的真實請求路徑。
使請求地址更加規范和簡潔,在開發過程中方法名有時候會很長,就可以直接使用路由進行簡潔處理。
可以統一對請求請求進行攔截并且進行權限檢查的操作。
并且在5.1版本支持了注解路由,方便在開發的過程中進行調試。
方便直接對請求進行緩存,并且還支持了路由中間件。
接下來咔咔會對路由的方方面面進行全面的解析,并且會給大家帶上腦圖方便大家最直觀的預覽。
一、路由初識化簡單分析
在框架執行流程那一篇文章中,都知道路由初始化是在初始化應用那個過程中執行的。
然后進入到routeInit這個方法,進行代碼解析。
來到這個方法先看代碼注釋,注釋為導入路由定義規則。
這段代碼的全部我給復制出來了,接下來就是對這段代碼進行解析。
/*** 路由初始化 導入路由定義規則* @access public* @return void*/public function routeInit(){// 路由檢測// scandir:返回置頂目錄的文件數組形式$files = scandir($this->routePath);foreach ($files as $file) {if (strpos($file, '.php')) {$filename = $this->routePath . $file;// 導入路由配置$rules = include $filename;if (is_array($rules)) {$this->route->import($rules);}}}if ($this->route->config('route_annotation')) {// 自動生成路由定義if ($this->appDebug) {$suffix = $this->route->config('controller_suffix') || $this->route->config('class_suffix');$this->build->buildRoute($suffix);}$filename = $this->runtimePath . 'build_route.php';if (is_file($filename)) {include $filename;}}}首先會獲取route目錄下的文件,函數scandir會返回指定目錄的文件并且用數組形式返回。
這里返回結果有三個,第一個為當前目錄,第二個為父級目錄。這倆個數據不用過多追究。
接著就會將route目錄下存在php結尾的文件給導入進來,也就是route.php文件。
當把路由文件導入進來之后,進行了一次判斷然后進行導入規則。
但是在路由文件可以看到是沒有返回任何數據的。
路由文件的return演示
那么這里的return是干嘛的呢!在5.1版本之前是沒有這一操作的,但是在5.1是存在的,接下來咔咔給大家演示一下這個使用方法。
首先在路由文件的return中配置一條數據。
然后在index文件中創建一個新的方法vpn
此時可以直接訪問路由vpn即可。
當route文件的return存在的數據的時候,就會執行到$this->route->import($rules);這一步,本節暫時不對這里做出探討,會在后文中大家詳細說明。
下半截源碼閱讀
第一行代碼知道這里的數據是從哪里來的嗎?
在應用初始化的initialize方法中,執行了init方法,在init方法中執行了容器中實例進行配置更新,在哪里進行設置的。如下圖
也就說在這里給路由添加的配置,并且可以看到路由的配置文件是來源于app配置文件。
想要知道這個配置是干什么的就需要從根源去追溯,所以就需要到app的配置文件中去尋找。
可以看到這個參數指的是注解路由的開關。
在緊接著就是根據注釋自動生成路由規則,這一塊的代碼暫時就說到這里,下文會對這些代碼進行詳細的說明。
二、通過定義路由再談門面
這里只是簡單提一下,關于路由的一些使用方法之類的就可以直接去文檔去看了,咔咔也不用做搬運工在搬過來。
先在路由文件定義一個kaka的路由。
但是這個get方法是怎么執行的呢!代碼追蹤也直接追不過去,那么這個時候應該怎么做。
第一種辦法,在框架中所有的基礎類都是注冊了門面模式的,所以在路由里邊引入facade就可以進行代碼追蹤了。
但是既然都把門面模式的源碼都看了怎么還會去使用第一種辦法,肯定要把其中的一些小細節給扒出來。
在base文件中已經注冊了門面的別名了,所以說在引入文件時可以使用use Config,也可以使用use think\facade\Config;
這個時候就會來到thinkphp/library/think/facade/Config.php這個文件。
可以看到是繼承了門面類的。
來到Facade這個類中,拉到文件最后可以看到一個方法__callStatic,這個方法是當調用不存在的靜態的方法時就會執行這個方法。
最后就會去執行創建門面類的方法,最終會通過容器返回返回對應的實例,比如使用的是Config,最終就會返回Config的這類的實例。
最后就會回到開始,使用call_user_func_array進行執行對應實例的方法。
static::createFacade()會返回Config的對應實例。
這一塊內容就說到這里了,雖然說之前在門面中也提到了但是在這里在回顧一下也是很有必要的,為故而知新嘛!
三、路由定義rule方法中的$this->group到底執行了什么
通過定義路由再談門面這一節中,就可以知道下圖中的rule方法會執行那個類的那個方法。
根據之前咔咔給大家介紹的方法就可以知道最終會執行到thinkphp/library/think/Route.php這個文件中的rule方法。
這里就會出現一個問題就是這個group是什么,是怎么執行的,其實在上邊的圖已經描述的很明白了。
咔咔在用文字給大家在說一遍,可以對照著進行查看源碼。
先看一下這個方法熟悉不熟悉。
沒錯這個方法在容器那一節中進行了特別的講解,如果不明白的去前言查看之前的文章進行查看。
那么這個方法在哪里執行的,咔咔就直接說了,就不說的那么細了。
當使用Route::rule();時就會執行到門面的__callStatic方法。
這個方法已經說了好多次了,當執行的靜態方法不存在時就會執行。
然后就會去執行createFacade這個方法。
在這個方法需要特別注意最后一行,也就是圈出來的哪一行代碼。
這里需要注意一下static::class,它返回的就是Route類,至于是怎么返回的之前也說過,如果不會的可以評論區見哈!
所以make方法的第一個參數就是Route。
接著就會進入到容器類的make方法,在這個方法中,注意咔咔圈的倆處。
在容器類中一定要注意的四個屬性,咔咔給大家畫了出來。
第一次進來會存入到容器標識名中。
并且第二次執行make方法時傳的是標識名的值。
直到這里就會執行到咔咔圈的第二處,執行invokeClass這個方法。
來到invokeClass方法中。
在這個方法中主要使用的是反射的知識。
首先會把傳過來的think\Route,進行反射類的實例化。
然后會去判斷think\Route中是否存在__make方法。
最后就會去執行Route類的__make方法。
并且進行依賴注入App類和Config類。
同時進行了實例化了本類就是代碼$route = new static($app, $config);
那么就會去執行構造函數。
接著來到構造函數的方法中查看。
這里一定要注意第二步的注釋,這個已經說了很多次了。
如果有疑問就是為什么使用了ArrayAccess。
咔咔就給大家捋一下,首先App類是繼承的Container類。
Container類又繼承著ArrayAccess類,所以就可以使用像之前的說,像數組一樣訪問對象。
- 注入的App類
- 此處使用了ArrayAccess像數組一樣訪問對象,但是$app中不存在request屬性,所以就會去執行容器類中的__get魔術方法,在__get方法中調用的是容器中的make方法,第一個參數為request,最終會返回request的實例。
- 注入的config類
- 此屬性返回域名,
- 初始化默認域名
接著就會來到方法setDefaultDomain。
這里需要注意一下咔咔圈的地方,在這一節的開頭就使用的這個屬性來執行的rule中的addRule方法。
這個group就是think\Route\Domain這個類。
又因為think\Route\Domain繼承著think\Route\Group
所以會執行到think\Route\Group到這個類里邊。
這下所有的流程就都已經理順了。
通過上面的這一頓分析和刨銑相信大家對這一塊的內容就十分了解了。
四、路由規則預處理
接下來的內容就是對路由規則的處理方式進行解析。
這幾個參數就對前倆個做一個簡單的打印,然后看一下這倆個參數分別都是什么。
參數rule
參數$route
可以看到這倆個參數幾個就是 路由的前半部分,一個是路由的后半部分。
從而就可以得知代碼會執行到下圖第一處圈起來的地方。
此時需要注意一點就是在上圖中圈起來的第二處地方,這個地方會在什么時候執行呢!
就是當下圖路由這樣設置的時候才會執行那段代碼。
這里的路由設置只是為了做演示,在實際工作中不能這樣設置路由啊!第二個路由地址會把第一個路由地址給覆蓋的。
接著代碼就會執行到創建路由規則實例,也就是下圖圈出來的地方。
關于創建路由規則實例的幾個參數需要進行簡單的介紹一下
第一個參數:$this->router
第二個參數:$this,就是指的think\route\Domain。
接下來的幾個地址就是路由規則和地址,就沒有必要看了,主要就是第一個參數。
接下來就需要進入到創建路由規則實例的方法中
代碼就會追蹤到thinkphp/library/think/route/RuleItem.php這個類中。
在這個類中做的事情就是設置規則,也就是下圖咔咔圈出來的地方。
路由規則預處理setRule方法解讀。
上圖中最后執行的流程就會來到setRule這個方法。
這段代碼沒有我們眼看這那么容器閱讀,接下來就跟著咔咔一步一步的去閱讀這塊代碼。
1.首先代碼就會執行這部分
從一開始就進行了一個簡單的判斷$,那么這個是從哪里過來的呢!
在文件thinkphp/library/think/route/RuleGroup.php這里就對$進行了處理。
所以說在什么情況下會執行預處理的第一部分呢!
我們來看一下簡單的案例。
然后在路由預處理中進行打印,就會發現打印結果后邊會多一個$符號。
substr介紹
這里對substr進行簡單的說明一下,碰到每一個知識點都簡單的回顧一下。
這個方法用來字符串截取。
參數一:需要截取的字符串
參數二:從什么位置開始截取,正數從左往右,負數結尾開始,0 - 在字符串中的第一個字符處開始
參數三:長度,正數從參數二進行返回,負數從末端進行返回。
所以說最終這塊代碼的執行會把$這個符號取消掉,只留為/,打印結果如下圖。
接著就是對路由預處理的下半部分進行解析。
先進行一次移除左側所有的斜杠的處理。
在接下來的$this->parent大家有有沒有一點點疑問,這里是指的哪里。
參數是從這里開始進行傳遞的。
經過上文的分析,我們知道$this->group就是實例化的think\route\Domain。
緊接著會跳轉到下圖,這里的$this就是就是think\route\Domain,要不就沒辦法解釋的。
這塊有問題的可以去看第三節文章。
所以說這里的$parent就是think\route\Domain這個類。
所以說這塊代碼就迎刃而解了。
所以說第三節哪里沒看明白的一定要自己拉著代碼好好的看,否則越往后越不明白。
這塊代碼具體做了什么事情后邊在深入詳解,這里就主要針對$this->parent它是怎么執行的就可以了。
在接著就是這塊代碼,這塊就是對理由參數做了倆中處理。
把帶有:符號的參數轉化。
例如::name 轉化為 <name>。
放在代碼中就是$this->rule = <name>。
五、解析生成路由標識的快捷訪問
從路由處理規則之后還有一步操作就是生成路由標識的快捷訪問。
路由規則中的變量
但是在前邊我們需要對路由規則中的變量做一點簡單的說明。
下圖圈出來的地方就是將要解析的內容。
來到parseVar這個方法,這個方法是在thinkphp/library/think/route/Rule.php這個類里,因為本類繼承的rule類。
那么接下來就是要對這一整塊的內容作出詳細的說明了。
首先要先明白preg_match_all的三個參數是什么。
參數一:需要搜索的字符串
參數二:輸入的字符串
參數三:多維數組,作為輸出參數輸出所有匹配結果, 數組排序通過flags指定。
所以說最終的匹配結果會存放在變量$matches中。
那么先來打印一下$matches這個變量。
這里需要先說明一個關于PHP版本的問題。
此次使用的路由案例為下圖
在打印這個值的時候,咔咔遇到了一點點問題,在PHP7.3.4中使用dump是無法顯示數據的,但是在dubug調試下會顯示。
在PHP版本7.2.9下dump是顯示的。
關于這個問題,咔咔就先繞過了,如果有大佬知道情況的可以給咔咔簡單的那么嘮一嘮,這里咔咔就使用PHP7.2.9來進行講解了。
后期咔咔在針對這個問題進行簡單的說明一下,遇到問題就需要去解決嘛!
那么你知道在上圖的打印結果中<name?>這個是怎么來的嗎?不知道的趕緊看下圖,在上文已經說過了。
在到這里說一次哈!
就是下圖為路由規則預處理的地方進行正則處理的,看到這里關于上面的那個問題,咔咔初步認為在不同版本中要木preg_replace這個方法進行了改動,要木就是關于正則方面進行了改動。
所以說把這塊搞定后,其余的代碼就是毛毛雨啦!
這里就是在根據?第一次出現的問題進行判斷。
在上文中咔咔給出的路由案例中相信大家也看到了另外一個跟案例很相似的路由。
這倆組路由參數分別為參數必有,和可選參數。
在打印截圖中咔咔只是說明了一種情況,另一種情況就需要大家去進行測試了。
最終會把結果返回給$var,接下來我們就光打印一下倆種情況的返回值就可以了。
情況一:Route::get(‘hello/[:name]’, ‘index/index/hello’);
情況二:Route::get(‘hello/:name’, ‘index/index/hello’);
從而可以得出結論就是,當參數可選時為2,必選時為1。
截止到這里關于路由規則中的變量一些處理就簡單說到這里。
這塊的代碼沒什么難的,只要會preg_match_all和strpos、substr就可以了。
解析生成路由標識的快捷訪問
接下來就繼續來看這塊內容。
咔咔下圖圈出來的就暫時先不用看,在下文中會單獨提出來來說這一塊。
那么接著就需要看一個$value這個值是什么了。
下圖就是打印結果,可以看到都包含了路由規則,參數是否可選,請求域名,請求方式。
最后又來到我們最熟悉的容器模塊了Container::get('rule_name')->set($name, $value, $first);,也就是這行代碼。
參數就是rule_name。
然后執行make方法,因為在容器類里邊已經把rule_name進行了綁定。
最終通過反射將object(think\route\RuleName)返回回來,并且存放在容器里邊。
此時就相當于是$this->instances['think\route\RuleName'] = object(think\route\RuleName);
最后一步就是設置路由規則。
截止到這里就把路由標識的快捷訪問就解析完了。
由于路由這塊的內容還有將近一半的內容,一文寫完篇幅有點太長
本文就到這里,下一文接著路由在來談其它內容。
六、總結
在這一文中主要針對路由的執行流程、通過定義路由的方式在一次對門面進行深究。
同時對rule方法中的group進行了特別詳細的講解,這塊的內容必須要好好的研究一下,類與類之間的繼承一定要注意。
在就是關于路由規則的預處理,就是對于路由的前半部分進行處理。
主要就是以下的三句話。
把帶有:符號的參數轉化。
例如::name 轉化為 。
放在代碼中就是$this->rule = <name>。
緊接著就是對生生路由標識的快捷訪問進行了源碼的閱讀,在這一節最重要的就是對變量的處理。
判斷參數是可選的還是不可選的,可選為2,不可選為1,作為標識。
以下是咔咔畫出來的腦圖。
這個腦圖還沒有寫完整,后邊還有內容,在下文會進行詳細的補充。
最后就是在給大家把這個圖給大家放出來,這個圖是最詳細的一個執行流程。
關于類與類之間的執行關系,都在圖中有詳細的描述。
堅持學習、堅持寫博、堅持分享是咔咔從業以來一直所秉持的信念。希望在偌大互聯網中咔咔的文章能帶給你一絲絲幫助。我是咔咔,下期見。
總結
以上是生活随笔為你收集整理的ThinkPHP路由源码解析(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: simulink 快捷键 运行_高效使用
- 下一篇: 平台币继续拉升,短期仍看涨