九色国产,午夜在线视频,新黄色网址,九九色综合,天天做夜夜做久久做狠狠,天天躁夜夜躁狠狠躁2021a,久久不卡一区二区三区

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費(fèi)電子書(shū)等14項(xiàng)超值服

開(kāi)通VIP
【Laravel系列6.4】管道過(guò)濾器

管道過(guò)濾器

通過(guò)之前的三篇文章,我們已經(jīng)學(xué)習(xí)完了服務(wù)容器相關(guān)的內(nèi)容,可以說(shuō),服務(wù)容器就是整個(gè) Laravel 框架的靈魂,從啟動(dòng)的第一步開(kāi)始就是創(chuàng)建容器并且加載所有的服務(wù)對(duì)象。而說(shuō)起管道,其實(shí)大家也不會(huì)太陌生,在程序開(kāi)發(fā)的世界中,管道模式的應(yīng)用隨處可見(jiàn),同樣在 Laravel 框架中,它也是核心一般的存在。甚至可以說(shuō),管道和服務(wù)容器的組合,才讓我們有了一個(gè)這樣的框架可以使用。

什么是管道

前面說(shuō)過(guò),管道模式非常常見(jiàn),為什么這么說(shuō)呢?

ps -ef | grep php

常見(jiàn)不?經(jīng)常用吧?這個(gè) Linux 命令就是一個(gè)管道命令。前面一條命令的結(jié)果交給后面一條命令來(lái)執(zhí)行,就像一條管道一樣讓這個(gè)命令請(qǐng)求的結(jié)果向下流動(dòng),這就是管道模式的應(yīng)用。

除了這個(gè)你還能想到什么呢?如果你跟過(guò)我的 PHP 設(shè)計(jì)模式系列的話(huà),那么 責(zé)任鏈模式 很明顯就是管道模式在 面向?qū)ο?語(yǔ)言中的應(yīng)用呀。


管道模式一般是和過(guò)濾器一起使用的,什么是過(guò)濾器呢?其實(shí)就是我們要處理請(qǐng)求的那些中間方法,比如說(shuō)上面命令中的 grep ,或者是 wc 、awk 這些的命令。大家其實(shí)很快就能發(fā)現(xiàn),在 Laravel 框架中,我們的中間件就是一個(gè)個(gè)的過(guò)濾器。而我們要處理的數(shù)據(jù),就是那個(gè) Request 請(qǐng)求對(duì)象。

Laravel 中管道的加載應(yīng)用

還記得我們?cè)诜?wù)容器中看到過(guò)的一個(gè) sendRequestThroughRouter() 方法嗎?另外在最早講中間件時(shí),我們也講過(guò)這里,我們?cè)賮?lái)看看它的代碼。

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

在這段代碼中,最后返回的那個(gè) Pipeline 對(duì)象就是一個(gè)管道對(duì)象。我們來(lái)看看它的這幾個(gè)方法是什么意思。

public function __construct(Container $container = null)
{
    $this->container = $container;
}

public function send($passable)
{
    $this->passable = $passable;

    return $this;
}

public function through($pipes)
{
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();

    return $this;
}

構(gòu)造函數(shù)、send() 和 through() 方法都比較簡(jiǎn)單,就是給當(dāng)前的對(duì)象中的屬性賦值,這個(gè)沒(méi)什么特別的。不過(guò)在 Pipeline 對(duì)象中,所有的方法都是會(huì) return 一個(gè) $this ,其實(shí)也就是實(shí)現(xiàn)了對(duì)象的鏈?zhǔn)秸{(diào)用。

重點(diǎn)在于 then() 方法。

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

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

這個(gè)方法也出乎意料的簡(jiǎn)單吧?里面只用了一個(gè) array_reduce() ,OK,到這里,你就可以和面試官吹牛了,Laravel 中的管道,或者說(shuō)中間件,其實(shí)最核心的就是這個(gè) array_reduce() 方法。要搞清楚 then() 方法是在干什么,我們就要先搞明白 array_reduce() 是在干嘛。

array_reduce

array_reduce() 這個(gè)函數(shù)在官方文檔的簽名是這樣的:

array_reduce(array $array, callable $callback, mixed $initial = null): mixed

它的作用是將回調(diào)函數(shù) callback 迭代地作用到 array 數(shù)組中的每一個(gè)單元中,從而將數(shù)組簡(jiǎn)化為單一的值。如果指定了可選參數(shù) initial,該參數(shù)將用作處理開(kāi)始時(shí)的初始值,如果數(shù)組為空,則會(huì)作為最終結(jié)果返回。

callback 這個(gè)回調(diào)函數(shù)會(huì)有兩個(gè)參數(shù),分別是 carry 攜帶上次迭代的返回值,如果迭代是第一次,那么這個(gè)值就是 initial 。另一個(gè)參數(shù)是 item ,也就是數(shù)組中的每個(gè)值。

看不懂吧?正常,我也看不懂,別慌,看例子。

function sum($carry, $item)
{
    $carry += $item;
    return $carry;
}

function product($carry, $item)
{
    $carry *= $item;
    return $carry;
}

$a = array(12345);
$x = array();

var_dump(array_reduce($a, "sum")); // int(15)
var_dump(array_reduce($a, "product"10)); // int(1200), because: 10*1*2*3*4*5
var_dump(array_reduce($x, "sum""No data to reduce")); // string(17) "No data to reduce"

這段代碼是官網(wǎng)上的例子。我們定義了一個(gè) sum() 方法用于累加,另外再定義了一個(gè) product() 方法用于階乘。前兩段測(cè)試的結(jié)果可以看出,通過(guò)將第一個(gè)數(shù)組傳遞進(jìn)去,然后調(diào)用 sum() 方法,我們完成了累加的功能,輸出了一個(gè)唯一的結(jié)果值。第二段則是增加了第三個(gè)參數(shù)給了個(gè)默認(rèn)的 10 ,結(jié)果就是多乘了一個(gè) 10 的累乘結(jié)果。而最后一段則是一個(gè)空的數(shù)組,返回的是 initial 給定的結(jié)果。

框架中 array_reduce 的參數(shù)

搞清楚了 array_reduce() 我們?cè)倩貋?lái)看看框架源碼中給出的參數(shù)。第一個(gè)參數(shù)是使用 array_reverse() 返回之后的 pipes 里面的內(nèi)容,這個(gè) pipes 是我們通過(guò) through() 方法傳遞進(jìn)來(lái)的。再回到 Kernel 中,我們會(huì)發(fā)現(xiàn)這個(gè)方法傳遞進(jìn)去的參數(shù)正是我們框架中加載的中間件 $middleware 成員變量。

之前的 bootstrap() 過(guò)程中,我們已經(jīng)將所有的 app/Http/Kernel.php 中注冊(cè)的中間件綁定注冊(cè)到了服務(wù)容器中。因此,這個(gè) pipes 數(shù)組中,就是我們所有的中間件信息。

接下來(lái)第二個(gè)參數(shù)是調(diào)用的一個(gè) carry() 函數(shù),它在 array_reduce() 方法中代表的是 callback 那個(gè)回調(diào)函數(shù)。

protected function carry()
{
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            try {
                if (is_callable($pipe)) {
                    return $pipe($passable, $stack);
                } elseif (! is_object($pipe)) {
                    [$name, $parameters] = $this->parsePipeString($pipe);

                    $pipe = $this->getContainer()->make($name);

                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    $parameters = [$passable, $stack];
                }

                $carry = method_exists($pipe, $this->method)
                                ? $pipe->{$this->method}(...$parameters)
                                : $pipe(...$parameters);

                return $this->handleCarry($carry);
            } catch (Throwable $e) {
                return $this->handleException($passable, $e);
            }
        };
    };
}

這個(gè)方法就復(fù)雜許多了。我們一步步的來(lái)看。

參數(shù)不用多說(shuō)了吧,stack 是上一次的返回值,pipe 是當(dāng)前我們要處理的值,也就是當(dāng)前的中間件對(duì)象。在這個(gè)回調(diào)函數(shù)中又調(diào)用了一層回調(diào)函數(shù),并將這兩個(gè)值通過(guò) use 傳遞進(jìn)去。而在里面的這個(gè)回調(diào)函數(shù)中,我們的參數(shù)是 passable 這個(gè)變量。這個(gè) passable 又是哪里來(lái)的?別急,我們先看這個(gè)函數(shù)內(nèi)部的實(shí)現(xiàn),最后會(huì)再說(shuō)到 passable 這個(gè)問(wèn)題。

進(jìn)入函數(shù)內(nèi)部的 try 代碼段中,第一個(gè)判斷,如果 pipe 是一個(gè)回調(diào)函數(shù),直接調(diào)用它并返回;第二個(gè)判斷,如果 pipe 不是一個(gè)對(duì)象而是一個(gè) string 的話(huà),解構(gòu) pipe 信息,服務(wù)容器 make 它,并且準(zhǔn)備好參數(shù);最后一個(gè) else 也就是 pipe 是一個(gè)對(duì)象,那么將 passable 和 stack 作為它的參數(shù)。最后,如果對(duì)象都有了,就會(huì)統(tǒng)一調(diào)用對(duì)象的 handle 方法,這個(gè)方法名也就是 $this->method 屬性定義的方法名。在最底下 $carry 調(diào)用對(duì)象或者回調(diào)函數(shù)的執(zhí)行方法。handle 熟悉不?我們自定義中間件時(shí),要實(shí)現(xiàn)的就是這個(gè)方法。參考:【Laravel系列3.4】中間件在路由與控制器中的應(yīng)用 https://mp.weixin.qq.com/s/9340q7F_hKrrxgf4o1LNMw

最終返回的就是這個(gè) $carry 變量,它是啥?中間件中 return next() 的東西呀,管道中的下一個(gè)回調(diào)函數(shù)。

上面的代碼我們是嵌套了兩層的回調(diào)函數(shù),通過(guò)之間的學(xué)習(xí),我們知道回調(diào)函數(shù)是有延遲加載的特性的,也就說(shuō),這一堆代碼是在我們最終調(diào)用這個(gè)回調(diào)函數(shù)的時(shí)候才會(huì)觸發(fā)的,那么它是在什么時(shí)候調(diào)用的呢?

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

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

沒(méi)錯(cuò),then() 方法最后的這個(gè) return 這里,現(xiàn)在知道 passable 是從哪里傳遞進(jìn)去的了吧。注意,這個(gè) passable 和最后那個(gè)默認(rèn) initial 參數(shù),都是我們當(dāng)前的請(qǐng)求 Request 對(duì)象和路由 Route 對(duì)象。也就是說(shuō),在整個(gè) Laravel 框架中,我們管道中流動(dòng)的,正是我們的 Request 對(duì)象,而最后返回的,則是各個(gè)中間件以及控制器處理完成之后的 Response 對(duì)象。中間件、控制器甚至路由,其實(shí)都是我們管道中的一個(gè)個(gè)的過(guò)濾器,根據(jù)我們的條件情況以及業(yè)務(wù)情況,可以隨時(shí)中斷或者對(duì)請(qǐng)求進(jìn)行處理,這下也就理解了什么我們可以在中間件返回,也可以在路由直接返回頁(yè)面結(jié)果了吧。

好吧,學(xué)習(xí)一個(gè)管道,其實(shí)我們又把整個(gè)請(qǐng)求響應(yīng)流程梳理了一遍。收獲滿(mǎn)滿(mǎn)吧!

直接寫(xiě)一個(gè)管道應(yīng)用來(lái)測(cè)試

直接調(diào)試管道可能比較復(fù)雜,因?yàn)?Laravel 框架加載的內(nèi)容非常多,不過(guò)我們可以自己寫(xiě)一個(gè)管道應(yīng)用來(lái)測(cè)試,并且可以設(shè)置斷點(diǎn)來(lái)方便地調(diào)試。

首先,我們需要定義幾個(gè)過(guò)濾器,也就是我們的中間件啦,不過(guò)我們不需要去實(shí)現(xiàn) Laravel 規(guī)范的,只需要有 handle() 方法就可以了。

class AddDollar
{
    public function handle($text, $next){
        return $next("$".$text."$");
    }
}

class AddTime
{
    public function handle($text, $next){
        $t = $next($text);
        return $t . time();
    }
}

class EmailChange
{
    public function handle($text, $next){
        return $next(str_replace("@""#", $text));
    }
}

沒(méi)有什么特殊的功能,我們過(guò)濾掉 Email 中的 @ 符號(hào)變成 # 號(hào),這個(gè)很多網(wǎng)站有會(huì)這樣的功能,避免被爬取 Email 地址。另外兩個(gè)就是增加符號(hào)和時(shí)間戳。在 AddTime 的處理中,我們使用的是 后置 中間件的功能,也就是在中間件完成處理后再添加內(nèi)容。這個(gè)在中間件相關(guān)的課程中我們也已經(jīng)講過(guò)了。

接下來(lái),就是使用管道來(lái)進(jìn)行處理。

Route::get('pipeline/test1'function(){
    $pipes = [
        \App\PipelineTest\EmailChange::class,
        \App\PipelineTest\AddTime::class,
        new \App\PipelineTest\AddDollar(),
        function($text, $next){
            return $next("【".$text."】");
        },
    ];

    return app(\Illuminate\Pipeline\Pipeline::class)
        ->send("測(cè)試內(nèi)容看看替換Email:zyblog@zyblog.ddd")
        ->through($pipes)
        ->then(function ($text) {
            return $text . "end";
        });
    // $【測(cè)試內(nèi)容看看替換Email:zyblog#zyblog.ddd】$end1630978948

});

在這段測(cè)試代碼中,我們對(duì) pipes 數(shù)組使用了類(lèi)字符串、實(shí)例對(duì)象、回調(diào)函數(shù)三種方式來(lái)實(shí)現(xiàn)中間件過(guò)濾器,可以看到最后的輸出結(jié)果正是我們想要的內(nèi)容。

大家可以在這里設(shè)置斷點(diǎn)然后進(jìn)入到 Pipeline 中查看這些中間件是如何調(diào)用運(yùn)行的,為什么要使用 array_reverse() 反轉(zhuǎn)中間件的順序,為什么后置中間件會(huì)在最后才去添加數(shù)據(jù)內(nèi)容。這一塊的調(diào)試就留給大家自己來(lái)吧!

總結(jié)

服務(wù)容器、管道(中間件)可以說(shuō)是 Laravel 框架中最最核心的內(nèi)容,也可以說(shuō)整個(gè)框架就是建立在這兩個(gè)模式之下的。對(duì)于服務(wù)容器的理解,就是要解決類(lèi)的依賴(lài)問(wèn)題,而對(duì)于管道的理解,則是要解決請(qǐng)求和響應(yīng)的數(shù)據(jù)流問(wèn)題。本身我們做 Web 開(kāi)發(fā),實(shí)際上就是在做對(duì)請(qǐng)求和響應(yīng)這兩條數(shù)據(jù)流的各種操作而已。

理解了最核心的兩部分內(nèi)容之后,下篇文章的課程中我們?cè)賮?lái)看看在 Laravel 中非常常用的 門(mén)面 功能是怎樣實(shí)現(xiàn)的。

參考文檔:

Laravel 中的 Pipeline — 管道設(shè)計(jì)范式 :https://learnku.com/laravel/t/7543/pipeline-pipeline-design-paradigm-in-laravel

Laravel 管道流原理 :https://learnku.com/articles/5206/the-use-of-php-built-in-function-array-reduce-in-laravel

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶(hù)發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
php調(diào)用Linux系統(tǒng)常用命令
PHP執(zhí)行l(wèi)inux命令常用函數(shù)匯總
js數(shù)組的五種迭代方法及兩種歸并方法
計(jì)算一段日期內(nèi)的周末天數(shù)的php代碼(星期六,星期日總和)
什么情況下會(huì)使用array.reduce函數(shù)
Laravel框架表單驗(yàn)證詳解
更多類(lèi)似文章 >>
生活服務(wù)
熱點(diǎn)新聞
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服