Laravel請求處理管道理解

lemon_lyue發表於2020-10-20

必備知識點:
閉包(Closure)array_reduce()函式array_reverse()函式call_user_func()函式
裝飾者模式
資料:
《Laravel框架關鍵技術解析》
相關部落格

示例程式碼:

interface Middleware
{
    public static function handle(Closure $next);
}

class VerifyCsrfToken implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "驗證 CSRF Token<br />";
        $next();
    }
}

class ShareErrorsFromSession implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "如果 Session中有'errors'變數, 則共享它<br />";
        $next();
    }
}

class StartSession implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "開啟 Session,獲取會話資料<br />";
        $next();
        echo "儲存會話資料,關閉 Session<br />";
    }
}

class AddQueuedCookiesToResponse implements Middleware
{
    public static function handle(Closure $next)
    {
        $next();
        echo "新增下一次請求需要的cookie".'<br>';
    }
}

class EncryptCookies implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "對輸入 Cookie 進行解密<br />";
        $next();
        echo "對輸出 Cookie 進行加密";
    }
}

class CheckMaintenanceMode implements Middleware
{
    public static function handle(Closure $next)
    {
        echo "檢測系統是否處於維護狀態<br />";
        $next();
    }
}

function getSlice()
{
    return function ($stack, $pipe) {
        return function () use ($stack, $pipe) {
            return $pipe::handle($stack);
        };
    };
}

function then()
{
    $pipes = [
        CheckMaintenanceMode::class,
        EncryptCookies::class,
        AddQueuedCookiesToResponse::class,
        StartSession::class,
        ShareErrorsFromSession::class,
        VerifyCsrfToken::class,
    ];

    $firstSlice = function () {
        echo "請求向路由器傳遞,返回響應<br />";
    };
    $pipes = array_reverse($pipes);
    call_user_func(
        array_reduce($pipes, getSlice(), $firstSlice)
    );
}

then();

執行流程

  1. 剛開始執行,首先array_reservse()函式將$pipes陣列倒序反轉,至於為什麼需要反轉後面再說明。
  2. array_reduce()將回撥函式getSlice()迭代地作用到$pipes陣列中的每一個單元中,而array_reduce()設定了初始值,則第一個執行的是$firstSlice這個函式。
  3. 第一個執行的是$firstSlice這個函式,所以
    function ($firstSlice, VerifyCsrfToken::class) {
     return function ($firstSlice, VerifyCsrfToken::class) {
         return VerifyCsrfToken::handle($firstSlice);
     }
    }
  4. 執行到第二個是時候是
    function (VerifyCsrfToken::class, ShareErrorsFromSession::class) {
     return function (VerifyCsrfToken::class, ShareErrorsFromSession::class) {
         return ShareErrorsFromSession::handle(function ($firstSlice, VerifyCsrfToken::class) {
             return function ($firstSlice, VerifyCsrfToken::class) {
                 return VerifyCsrfToken::handle($firstSlice);
             }
         });
     }
    }
  5. 以此類推….
    最後變成這樣:
    function (){
     return CheckMaintenanceMode::handle(function (){
         return EncryptCookies::handle(function (){
             return AddQueuedCookiesToResponse::handle(function (){
                 return StartSession::handle(function (){
                     return ShareErrorsFromSession::handle(function (){
                         return VerifyCsrfToken::handle(function (){
                             echo "請求向路由器傳遞,返回響應<br />";
                         })
                     })
                 });
             });
         });
     });
    }
    執行到陣列的最後一次,則是CheckMaintenanceMode::class,然後call_user_func()執行回撥函式,先執行CheckMaintenanceMode::class,然後執行EncryptCookies::class,執行順序則與未反轉前的陣列順序完全相同,這也是為什麼需要使用array_reservse()函式進行反轉的原因。

    執行結果

    檢測系統是否處於維護狀態
    對輸入 Cookie 進行解密
    開啟 Session,獲取會話資料
    如果 Session中有'errors'變數, 則共享它
    驗證 CSRF Token
    請求向路由器傳遞,返回響應
    儲存會話資料,關閉 Session
    新增下一次請求需要的cookie
    對輸出 Cookie 進行加密
本作品採用《CC 協議》,轉載必須註明作者和本文連結
lemon_lyue

相關文章