成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入理解Laravel中間件

xiaotianyi / 1102人閱讀

摘要:中的中間件是中的一個重點本篇將從源碼的角度去講解中的中間件洞察中的中間件是如何運行的明白為何我們使用中間件的時候要進行那些步驟本篇文章假設讀者已經掌握中間件的基本用法如果不了解其用法可以移步查看中間件的使用我們都知道使用中間件有三個步驟使用

Laravel中的中間件是laravel中的一個重點,本篇將從源碼的角度去講解Lravel中的中間件,洞察Laravel中的中間件是如何運行的,明白為何我們使用中間件的時候要進行那些步驟. 本篇文章假設讀者已經掌握中間件的基本用法,如果不了解其用法,可以移步查看laravel中間件的使用

我們都知道,使用Laravel中間件有三個步驟:

使用php artisan生成一個中間件,這里假設生成一個TestMiddleware的中間件

重寫TestMiddleware中的handle函數(shù),其中代碼邏輯寫在return $next($request);之前或者之后表示在執(zhí)行請求之前或者之后運行這段代碼.

app/Http/Kernel.phprouteMiddleware注冊一個中間件

然而,這上面的幾點下來,你會不會一頭霧水,為什么要執(zhí)行這么些操作之后才能使用一個中間件.尤其是第二點?

中間件實現(xiàn)代碼

你一定聽過Laravel中間件的概念跟裝飾器模式很像.簡單來講,裝飾器模式就是在開放-關閉原則下動態(tài)的增加或者刪除某一個功能.而Laravel的中間件也差不多是這個道理:

一個請求過來,在執(zhí)行請求之前,可能要進行Cookie加密,開啟回話,CSRF保護等等操作.但是每一個請求不一定都需要這些操作,而且,在執(zhí)行請求之后也可能需要執(zhí)行一些操作.我們需要根據(jù)請求的特性動態(tài)的增加一些操作.這些需求正好可以使用裝飾器模式解決.

但是,Laravel中的中間件在代碼實現(xiàn)上跟中間件 又有點區(qū)別,這里給出一段代碼.真實的模擬了Laravel中間件的工作流程.

";
        $next();
    }
}


class ShowErrorsFromSession implements Milldeware {

    public static function handle(Closure $next)
    {
        echo "共享session中的Error變量 
"; $next(); } } class StartSession implements Milldeware { public static function handle(Closure $next) { echo "開啟session
"; $next(); echo "關閉ession
"; } } class AddQueuedCookieToResponse implements Milldeware { public static function handle(Closure $next) { $next(); echo "添加下一次請求需要的cookie
"; } } class EncryptCookies implements Milldeware { public static function handle(Closure $next) { echo "解密cookie
"; $next(); echo "加密cookie
"; } } class CheckForMaintenacceMode implements Milldeware { public static function handle(Closure $next) { echo "確定當前程序是否處于維護狀態(tài)
"; $next(); } } function getSlice() { return function($stack,$pipe) { return function() use($stack,$pipe){ return $pipe::handle($stack); }; }; } function then() { $pipe = [ "CheckForMaintenacceMode", "EncryptCookies", "AddQueuedCookieToResponse", "StartSession", "ShowErrorsFromSession", "VerfiyCsrfToekn" ]; $firstSlice = function() { echo "請求向路由傳遞,返回相應
"; }; $pipe = array_reverse($pipe); $callback = array_reduce($pipe,getSlice(),$firstSlice); call_user_func($callback); } then();

運行代碼,輸出

確定當前程序是否處于維護狀態(tài) 
解密cookie 
開啟session 
共享session中的Error變量 
驗證csrf Token 
請求向路由傳遞,返回相應 
關閉ession 
添加下一次請求需要的cookie 
加密cookie 

這段代碼可能有點難懂,原因在于對于閉包函數(shù)(Closure),array_reduce以及call_user_fun函數(shù),而且函數(shù)調用過程又是遞歸,可以嘗試使用xdebug來調試執(zhí)行.這里只提一點,array_reduce中第二個參數(shù)是一個函數(shù),這個函數(shù)需要兩個參數(shù):

第一個參數(shù)從array_reduce的第一個參數(shù)$pipe數(shù)組中獲得

第二個參數(shù)為上一次調用的返回值.這個例子里面返回值是一個閉包函數(shù).

如果還是不懂可以看這個例子.當理解這段代碼之后,你會發(fā)現(xiàn)使用Laravel中間件步驟中的第2步瞬間就明白了.

源碼解析

好了,通過上面的代碼,我們已經解決了第二個問題.現(xiàn)在看看為什么使用中間件之前需要在app/Http/Kernel.php注冊中間件.
我們知道,所謂注冊,也只是在$routeMiddleware數(shù)組中添加一項而已.

要想知道,為什么中間件注冊完之后就可以使用,我們需要從源碼的角度去看看Laravel在底層為我們做了什么.

從Index.php文件分析,這里我們不分析Laravel源碼的全部,只講解有關中間件的部分,關于Laravel的分析,請關注我的其他文章.并且,這里只講解全局中間件的運行過程,因為路由中間件和全局中間件的運行流程是一樣的,但是路由中間件.涉及到路由分發(fā)過程,本次分析只專注于中間件,等到講解路由的時候再對路由中間件進行展開分析.

有關中間件的步驟從Index.php的handle函數(shù)開始.

$kernel = $app->make(IlluminateContractsHttpKernel::class);

$response = $kernel->handle(
    $request = IlluminateHttpRequest::capture()
);

從這個函數(shù)開始處理一個請求,注意這里kernel有一個繼承鏈,handle函數(shù)的真正實現(xiàn)在vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php文件中.

轉到Http/Kernel.php類,在看handle函數(shù)之前,我們發(fā)現(xiàn)這類有兩個protected的成員:$middleware$routeMiddleware.看到這個我們就可以聯(lián)想到我們注冊中間件的那個文件app/Http/Kernel.php,這個文件正是繼承Illuminate/Foundation/Http/Kernel.php的,所以我們明白了,當我們在app/Http/Kernel.php注冊的全局中間件會在這里被處理.

接著,我們看handle函數(shù),調用了sendRequestThroughRouter函數(shù),進入這個函數(shù),我們看到其函數(shù)體

{
    $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());
}

可以看到這你實例化了一個Pipeline.這個可以稱之為管道,如果懂得linux中的管道概念的話,那么就可以理解這里命名為Pipeline的原因:客戶端發(fā)過來的請求被一個又一個的中間件處理,前一個中間件處理往之后的結果交給了下一個中間價,類似管道一樣.

pipeline之后調用了三個函數(shù)send, through, then.這三個函數(shù)分別做了

傳遞客戶端請求requestPipeline對象

傳遞在app/Http/Kernel.php中定義的全局中間件到Pipeline對象

執(zhí)行中間件,其中then函數(shù)的參數(shù),$this->dispatchToRouter()返回的是一個回調函數(shù),這個函數(shù)可以類比為我們示例代碼中輸出請求向路由傳遞,返回相應的函數(shù), 因為這里涉及到路由的工作流程,所以暫時這么理解,等到了分析路由的時候,我們再綜合起來.

接下來,我們來看then函數(shù)的代碼

public function then(Closure $destination)
{
    $firstSlice = $this->getInitialSlice($destination);
    $pipes = array_reverse($this->pipes);

    return call_user_func(
        array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
    );
}

發(fā)現(xiàn)then函數(shù)的代碼跟我們上面的示例代碼有點類似,其中 :

$pipe就是保存了在app/Http/Kernel.php中定義的全局中間件,具體邏輯可以看through函數(shù)

$this->passable中保存的是客戶端請求的實例對象requset.具體邏輯可以從send函數(shù)看到

getInitialSlice調用的函數(shù)只是對原有的destination添加了一個$passable的參數(shù).這個$passabel就是請求實例.

protected function getInitialSlice(Closure $destination)
{
    return function ($passable) use ($destination) {
        return call_user_func($destination, $passable);
    };
}

了解了then函數(shù)里的所有信息, 下面執(zhí)行的操作就跟我們上面的示例代碼一樣了,只要理解了上面代碼的邏輯,這里Laravel的代碼也是這么工作的,唯一不同的地方在于getSlice返回的閉包函數(shù),從上面的例子可以知道返回的閉包函數(shù)才是調用中間件的核心,我們來看下getSlice到底是怎么工作的

protected function getSlice()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            // If the pipe is an instance of a Closure, we will just call it directly but
            // otherwise we"ll resolve the pipes out of the container and call it with
            // the appropriate method and arguments, returning the results back out.
            if ($pipe instanceof Closure) {
                return call_user_func($pipe, $passable, $stack);
            } else {
                list($name, $parameters) = $this->parsePipeString($pipe);

                return call_user_func_array([$this->container->make($name), $this->method],
                                            array_merge([$passable, $stack], $parameters));
            }
        };
    };
}

getSlice函數(shù)的大體邏輯跟我們上面實例代碼的邏輯差不多,只是我們上面調用在中間件的handle函數(shù)的時候直接使用$pipe::handle($stack);,因為中間件里面的函數(shù)是靜態(tài)函數(shù).而在Laravel中,這里我們只是傳遞了要實例化的中間件的類名,所以在getSlice里面還要去實例化每個要執(zhí)行的中間件,

list($name, $parameters) = $this->parsePipeString($pipe);

return call_user_func_array([$this->container->make($name), $this->method], array_merge([$passable, $stack], $parameters));
                

上面兩句代碼中,第一句根據(jù)中間件的類名去分離出要實例化的中間件類,和實例化中間件可能需要的參數(shù),

然后call_user_func_array里面由于柔和了幾行代碼,所以這里分解一下,函數(shù)包含兩個參數(shù) :

[$this->container->make($name), $this->method]為調用某個類中方法的寫法,其中$this->container->make($name)是使用服務容器去實例化要調用的中間件對象,$this->method就是handle函數(shù)

array_merge([$passable, $stack], $parameters)為調用中間件所需要的參數(shù),這里我們可以看到調用中間件的handle必然會傳遞兩個參數(shù):$passable(請求實例$request)和下一個中間件的回調函數(shù)$stack

到這里,Laravel中間件的部分就結束了,這部分代碼有點難以理解,尤其是一些具有函數(shù)式特性的函數(shù)調用,比如array_reduce,以及大量的閉包函數(shù)和遞歸調用,大家一定要耐心分析,可以多使用dd函數(shù)和xdebug工具來逐步分析.

這里扯一句,函數(shù)then里面可以看到有getSlice$firstSlice的命名,這里slice是一片的意思,這里這樣子的命名方式是一種比喻 : 中間件的處理過程就上面講的類似管道,處理中間件的過程比作剝洋蔥,一個中間件的執(zhí)行過程就是剝一片洋蔥.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://systransis.cn/yun/22126.html

相關文章

  • 深入理解 Laravel Eloquent(二)——中間操作流(Builder)

    摘要:原文發(fā)表在我的個人網(wǎng)站深入理解二中間操作流本篇教程是該系列教材的第二篇,將主要講述中中間操作流的概念。復雜用法示例下一步深入理解三模型間關系關聯(lián) 原文發(fā)表在我的個人網(wǎng)站:深入理解 Laravel Eloquent(二)——中間操作流(Builder) 本篇教程是該系列教材的第二篇,將主要講述 Eloquent 中中間操作流的概念。中間操作流是我自己總結并翻譯的概念,支撐該功能的類...

    yy736044583 評論0 收藏0
  • 深入理解 Laravel 管道

    摘要:將請求傳入到指定的中間件路由。用于處理任務的方法接收兩個參數(shù),第一個是一個可傳遞的對象,第二個是閉包,在運行最后一個管道后對象將被重定向到這個閉包。我希望這個實例能夠讓你對有更深如的了解,并知道如何使用它們。 這是一篇譯文,原文 Understanding Laravel Pipelines。譯文首發(fā)于 深入理解 Laravel 管道,轉載請注明出處。 基本上,你可以使用 larave...

    paraller 評論0 收藏0
  • Laravel 深入核心系列教程

    摘要:前言年底了不太忙,最近一段時間也一直在研究,就想寫篇關于比較深一點的教程系列啥的,于是就找到站長給開了寫教程的渠道。優(yōu)點的就是為藝術家創(chuàng)造的框架,它也是工程化的趨勢。項目維護方便也是事實。如果有遇到問題可以直接在教程下面留言。 前言 年底了不太忙,最近一段時間也一直在研究laravel,就想寫篇關于laravel比較深一點的教程系列啥的,于是就找到站長給開了寫教程的渠道。由于第一次寫,...

    wemall 評論0 收藏0
  • 深入理解 Laravel Eloquent(三)——模型間關系(關聯(lián))

    摘要:是什么是一個,全稱為,翻譯為對象關系映射如果只把它當成數(shù)組庫抽象層那就太小看它了。所謂對象,就是本文所說的模型對象關系映射,即為模型間關系。至此,深入理解系列文章到此結束。 原文發(fā)表在我的個人網(wǎng)站:深入理解 Laravel Eloquent(三)——模型間關系(關聯(lián)) 在本篇文章中,我將跟大家一起學習 Eloquent 中最復雜也是最難理解的部分——模型間關系。官方英文文檔中...

    2501207950 評論0 收藏0
  • laravel框架應用和composer擴展包開發(fā)

    摘要:官方地址是目前最流行的框架,發(fā)展勢頭迅猛,應用非常廣泛,有豐富的擴展包可以應付你能想到的各種應用場景,框架思想前衛(wèi),跟隨時代潮流,提倡優(yōu)雅代碼,自稱為工匠,其中的模板引擎容器以及擴展包為業(yè)務的開發(fā)提供了極大的便利。 laravel5.5+ laravel官方地址 laravel是目前最流行的php框架,發(fā)展勢頭迅猛,應用非常廣泛,有豐富的擴展包可以應付你能想到的各種應用場景,lara...

    shevy 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<