老當機帶你深入理解 Laravel 之驗證器上(控制器引數解析)

Dennis_Ritchie發表於2019-12-30

前言

今天是2019年12月30號了,哈哈,舊的一年即將過去,新的一年即將來臨。老司機繼續帶你在裝逼的道路上越走越遠,沒錯,我們要做一個硬核的裝逼俠。

準備工作

由於我現在講解的每一部分內容都不是獨立存在的,與前面講解的內容有關聯,所以我希望你至少閱讀過一下2篇博文,這是必要的:

  1. Laravel 之路由分發
  2. Laravel 之路由匹配

程式碼講解

我們直接進入到Illuminate\Routing\Router類的:

老當機帶你深入理解Laravel之驗證器

直接呼叫dispatchToRoute方法,很簡單:

老當機帶你深入理解Laravel之驗證器

呼叫runRoute方法:

老當機帶你深入理解Laravel之驗證器

繼續呼叫runRouteWithinStack方法:

老當機帶你深入理解Laravel之驗證器

上面貼出的程式碼,我已經在Laravel 之路由分發Laravel 之路由匹配講解過多次了,不熟悉的可以再次回去看一遍,不在多講,我們直接看$route->run(),即呼叫
Illuminate\Routing\Route類的run方法:

老當機帶你深入理解Laravel之驗證器

這裡我們只關心路由中指定控制器的情況,假如說我在web.php檔案中,我定義了一個路由:

Route::get('/debug/args/{id}/{name}', 'Admin\DebugController@args');

請大家記住這個路由,後面我們會用到,注意了我在路由中指定了2個引數,分別是idname,控制器是DebugController,動作是args。關於DebugController的定義如下:

class  DebugController extends Controller
{

    public function args(DebugFormRequest $request, $name, $id)
    {
        dd($name, $id);
    }

}

這裡先不多說,我們回到Illuminate\Routing\Route類的run方法,繼續呼叫runController方法:

老當機帶你深入理解Laravel之驗證器

這裡的controllerDispatcher方法返回的是Illuminate\Routing\ControllerDispatcher類的物件,關於這個我也在之前的2篇博文中說過了,我們進入到它的dispatch方法:

老當機帶你深入理解Laravel之驗證器

上面我標出來的就是今天分析的重點,$route->parametersWithoutNulls()返回的當前路由的引數,比如對於前面定義的路由,當我訪問它時,http://localhost/debug/args/2/DennisRitche,此時我列印它的結果:

老當機帶你深入理解Laravel之驗證器

這個很好理解,接下來,我們繼續分析resolveClassMethodDependencies方法,這個方法屬於Illuminate\Routing\RouteDependencyResolverTrait 這個trait的,因為當前的ControllerDispatcher使用到了這個trait,我們來看它:

老當機帶你深入理解Laravel之驗證器

我們進入到resolveClassMethodDependencies方法中:

老當機帶你深入理解Laravel之驗證器

這個方法用於解析當前控制器方法的依賴性引數,1開始檢測控制器是否存在這個方法,2才真正開始解析引數依賴,我們進入到resolveMethodDependencies方法中,這裡有必要提醒一下,如果你對PHP的反射不清楚的話,請檢視我之前寫的這篇詳解 PHP 反射的基本使用

老當機帶你深入理解Laravel之驗證器

這個方法本身並不複雜,但是我不得不吐槽Laravel自身設計上的一點坑,為啥我怎麼說,當我訪問/debug/args/2/DennisRitche這個路由的時候,控制器的列印結果如下:

老當機帶你深入理解Laravel之驗證器

但是,關鍵的問題是,這不是我想要的,因為我的控制器方法是這麼定義的:

老當機帶你深入理解Laravel之驗證器

所以列印的結果並不是我想要的,至於原因就在這個resolveMethodDependencies方法中,在這個方法中,迴圈遍歷$reflector->getParameters()返回的引數,每一個引數都是ReflectionParameter類的物件,transformDependency用於檢測當前的引數是不是一個類物件,如果是的話,就返回一個這個類的物件:

老當機帶你深入理解Laravel之驗證器

getClass方法獲取引數的類名,接下來的程式碼,就是根據它的類名建立物件,當然了首先還是判斷它是否存在預設值,返回到resolveMethodDependencies方法中,我們看,這段程式碼可能有些迷惑,我仔細給大家分析:

老當機帶你深入理解Laravel之驗證器

首先is_null判斷transformDependency返回的值是不是為空,如果不為空的話,$instanceCount值加1,這個值表示解析出的例項數,
$key表示當前的引數在引數中的位置,以DebugControllerargs方法為例:

老當機帶你深入理解Laravel之驗證器

因為第一個引數$requestDebugFormRequest類的物件,所以此時$key為0,因此$instanceCount的值為1。說到這裡,有必要再說下spliceIntoParameters方法,這個方法很簡單,但是在當前的分析中,很有必要解釋哈:

老當機帶你深入理解Laravel之驗證器

方法array_splice會在$parameters$offset位置插入一個元素,插入的值是$value
我們看此時插入$request的時候,檢視結果:

老當機帶你深入理解Laravel之驗證器

返回到resolveMethodDependencies接續迭代下一個引數,此時的引數為$name,關鍵的問題來了,程式碼會進入到elseif語句中,在分析它的程式碼前,我們來看哈,$values的值為:

老當機帶你深入理解Laravel之驗證器

控制檯列印:

老當機帶你深入理解Laravel之驗證器

好了,我們進入到elseif語句中,! isset($values[$key - $instanceCount])這個判斷語句是啥意思呢?前面我們說了$instanceCount表示解析出的例項數,而$key表示當前引數在控制器方法所有引數中的位置,這個地方表明Laravel會在所有的路由引數中依次取值,而不是按引數名進行取值,這就是我之前給大家說的Laravel坑,這就是為啥當我列印控制器引數的時候,$name值為2,$id引數為"DennisRitche"。如果! isset($values[$key - $instanceCount])的返回值為false,表示路由中無法解析出這個引數值,$parameter->isDefaultValueAvailable()檢查引數是否存在預設值,所以總結下elseif的判斷條件意思就是,當路由中不存在這個引數的時候,檢查這個引數是否存在預設值,如果存在的話,$parameter->getDefaultValue()會獲取預設值並把它儲存到$parameters的相對應位置。

好了,resolveMethodDependencies這裡關鍵的地方已經給大家解釋了。

解析完所有的引數之後,我們回到Illuminate\Routing\ControllerDispatcherdispatch方法中:

老當機帶你深入理解Laravel之驗證器

1檢測當前的控制器是否存在callAction方法,如果存在就呼叫它,如果不存在就直接呼叫對應的控制器方法。

總結

今天的這篇博文,我簡要的給大家講述了Laravel是如何解析控制器方法的引數的,這是下一篇給大家講解Laravel驗證器的基礎條件,希望大家能理解,歡迎加入qq群:

如果有不懂的地方,可以加我的qq:1174332406,或者是微信:itshardjs

相關文章