Laravel 原始碼解讀

nongnong發表於2019-03-29

最近想看看laravel的原始碼,感覺頭大,程式碼太多,層次太深,但是結構優美,語法基本代表php的標準,所以計劃花點時間好好閱讀一下,順便記下筆記,以便隨時檢視。但是我計劃先一個點一個點的弄懂,最後再做總結,可能順序不太友好,本人能力有限,若理解不到位的地方,希望大神指點,或者噴都可以,本人內心強大,希望結交更多志同道合的朋友一起學習交流進步。

laravel pipline

簡單介紹下中介軟體的作用過濾請求,長話短說,Illuminate\Foundation\Http\Kernel 裡面的方法140行左右

protected function sendRequestThroughRouter($request)
    {
        $this->app->instance('request', $request);

        Facade::clearResolvedInstance('request');

        $this->bootstrap();

        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }

看send (設定初始請求物件也就是$request),through(就是要過濾的中介軟體類) 和 then (就是返回一個閉包函式)方法 最主要是then方法 大家可以去看看

public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );

        return $pipeline($this->passable);
    }

laravel 的pipline 很巧妙 你可以用命令鏈模式去做pipline 但是laravel 巧妙地用到 array_reduce這個函式累積特性 加上i閉包函式的use 屬效能儲存作用域變數的特性,具體細節 我也不清楚,然後 我根據自己的理解 自己做了個例子

class a {
    public static function test($request,$next){
        return $next($request);
    }
}
class b {
    public static function test($request,$next){
        return $next($request);
    }
}
class c {
    public static function test($request,$next){
        return $next($request);
    }
}
$test = [a::class];
$prepareDestination = function ($a){var_dump($a);};
$pipline = array_reduce(
    array_reverse($test),
    function($stack,$pip){
        return function($passable) use($stack,$pip){
            print_r($static);
            $pip::test($passable, $stack);
        };
    },function($request)use($prepareDestination){$prepareDestination($request);});
    $pipline([1]);

我只是想用自己簡單的例子去理解它的pipline 因為它的初次看起來很心慌,原理差不多
接下來我寫一些上面的一段程式碼發生了什麼,如果大家去自己寫一箇中介軟體的話

public function handle($request, Closure $next)
    {
        if (!$request->user()) {

        }
        return $next($request);
    }

這裡的$next 其實就是閉包函式(上面的$pipline)只不過每次經過一箇中介軟體處理後 $next($request),相當於執行上一次返回的閉包,廢話少說,直接上圖 或者程式碼
先讓$test=[a::class];
然後我們 列印 $stack

例1
Closure Object
(
    [static] => Array
        (
            [prepareDestination] => Closure Object
                (
                    [parameter] => Array
                        (
                            [$a] => <required>
                        )

                )

        )

    [parameter] => Array
        (
            [$request] => <required>
        )

)

然後讓$test = [a::class,b::clas]

例2
Closure Object
(
    [static] => Array
        (
            [stack] => Closure Object
                (
                    [static] => Array
                        (
                            [prepareDestination] => Closure Object
                                (
                                    [parameter] => Array
                                        (
                                            [$a] => <required>
                                        )

                                )

                        )

                    [parameter] => Array
                        (
                            [$request] => <required>
                        )

                )

            [pip] => b
        )

    [parameter] => Array
        (
            [$passable] => <required>
        )

)
Closure Object
(
    [static] => Array
        (
            [prepareDestination] => Closure Object
                (
                    [parameter] => Array
                        (
                            [$a] => <required>
                        )

                )

        )

    [parameter] => Array
        (
            [$request] => <required>
        )

)

然後可以在加上$test = [a::class,b::class,c::class]; 大家去列印看看

下面說明一下具體流程
當array_reduce 第一次執行時候時候 執行第二個引數閉包 注意 此時 use裡面的 $stack(是第三個引數$prepareDestination) 和 $pip(倒轉後第一個 b::class) ,執行後返回一個閉包,上面倆引數被儲存起來了,接著執行第二次 注意此時的 use裡面的 $stack(是第一次返回的閉包) $pipa::class) 然後又返回一個閉包,這個閉包裡也儲存著上面倆引數 。
其實最後得到的$pipline 就是一個閉包(第二次返回的閉包$stack儲存著第一次返回的閉包依次類推)

$pipline = function($passable) use($stack,$pip){
            print_r($stack);
            //var_dump($pip);
            $pip::test($passable, $stack);
        }

不過此時$stack是一個閉包相當於例2 不過他的$stack是上一次返回的閉包,也等於

$stack = function($passable) use($stack,$pip){
            print_r($stack);
            //var_dump($pip);
            $pip::test($passable, $stack);
        }

不過此時的 use 裡面的 $stack 引數 也是上一次返回的閉包,依次類推,直到我們預設的$prepareDestination閉包
最後當我們執行
$pipline([1])時候 裡面的[1] 實際就是我們的$request, 也就執行裡面的 $pip::test($passable, $stack); 注意此時的$pip 是 a類, $passable 是 [1] , $stack我們第一次返回的閉包例2,然後執行a 中的 return $next($request);· 此時 $next 就是我們第一次返回的閉包, 執行第一次返回的閉包 , 還記得第一次閉包裡面的連引數儲存的什麼嗎? 其實上面說過 $stack(第零次返回的閉包預設是第三個引數$prepareDestination) 和 $pip(倒轉後第一個 b::class);所以一直執行下去;

最後總結一下 array_reduce 第一次生返回的閉包的倆use引數是預設的$prepareDestination和$test陣列倒轉後的第一個引數,array_reduce 第二次返回的閉包$pipline 的倆use引數是第一次返回的閉包和$test陣列倒轉後的第二個引數, 那麼執行$pipline 的時候第一次$next($request)
相當於執行第一次的返回的閉包,第一次閉包裡面也有$next($request) 相當於執行預設$prepareDestination 閉包; laravel 的原理跟這個差不多,這這是我自己的理解,理解有誤希望多多指教。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章