亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

Laravel Pipeline 組件的實(shí)現(xiàn)

psychola / 2241人閱讀

摘要:若能正常通過(guò)則繼續(xù)傳遞至最終的處理邏輯,如控制器的某個(gè)方法或者一個(gè)匿名函數(shù)。這里需要注意,返回的不僅僅是個(gè)匿名函數(shù),更是一個(gè)閉包,該閉包中引用了兩個(gè)外部值,分別是提供給第二參數(shù)中的回調(diào)的兩個(gè)參數(shù),即數(shù)組合并結(jié)果和當(dāng)前待合并的值。

Laravel 框架中有一個(gè)非常有趣的功能,就是 HTTP 中間件,我們?cè)诙x路由的時(shí)候,通過(guò)中間件對(duì)訪問(wèn)進(jìn)行過(guò)濾。來(lái)自外部的請(qǐng)求首先經(jīng)過(guò)全局中間件,若通過(guò),則會(huì)繼續(xù)穿過(guò)層層路由組所設(shè)置的中間件,在到達(dá)目的路由,當(dāng)然,目的路由也可能定義了個(gè)中間件,通過(guò)后,該路由的處理對(duì)象(如控制器),得到的就是一個(gè)經(jīng)過(guò)過(guò)濾的請(qǐng)求了。

開始

本文當(dāng)然不是討論中間件如何使用,而是其實(shí)現(xiàn)的基礎(chǔ)。Laravel 框架中有一個(gè)組件叫做 IlluminatePipeline,意味 “管道”,我們看看下面這個(gè)代碼示例:

 7) {
        return $poster;
    }

    $poster += 3;
    echo "pipe2: $poster
";
    return $next($poster);
};

$pipe3 = function ($poster, Closure $next) {
    $result = $next($poster);
    echo "pipe3: $result
";
    return $result * 2;
};

$pipe4 = function ($poster, Closure $next) {
    $poster += 2;
    echo "pipe4 : $poster
";
    return $next($poster);
};

$pipes = [$pipe1, $pipe2, $pipe3, $pipe4];

function dispatcher($poster, $pipes)
{
    echo "result: " . (new Pipeline)->send($poster)->through($pipes)->then(function ($poster) {
            echo "received: $poster
";
            return 3;
        }) . "
";
}

echo "==> action 1:
";
dispatcher(5, $pipes);
echo "==> action 2:
";
dispatcher(7, $pipes);

上述代碼執(zhí)行結(jié)果如下:

==> action 1:
pipe1: 6
pipe2: 9
pipe4 : 11
received: 11
pipe3: 3
result: 6
==> action 2:
pipe1: 8
result: 8
流程概覽

Pipeline 組件實(shí)現(xiàn)了一個(gè)過(guò)濾流程:

原始數(shù)據(jù) ---> 【前置管道】 ---> 目標(biāo)處理邏輯 ---> 【后置管道】 ---> 結(jié)果數(shù)據(jù)

通過(guò)這種機(jī)制,可以將目標(biāo)處理邏輯與過(guò)濾、認(rèn)證等機(jī)制的代碼分離開來(lái),這樣我們就更容易讓代碼清晰和易于維護(hù)。通過(guò)前置、后置管道,在其中 “放置” 我們需要過(guò)濾的邏輯即可,如上述代碼,雖然只是一個(gè)簡(jiǎn)單的示例,就已經(jīng)能夠看得出,整個(gè)流程的動(dòng)向,譬如我們?cè)谏厦媸纠袦?zhǔn)備了四個(gè)過(guò)濾組件(中間件): pipe1、pipe2、pipe3、pipe4,其中 1、2、4 是前置,3 為后置。

輸入的原始數(shù)據(jù)為 5,執(zhí)行過(guò)程首先通過(guò) 1 號(hào)過(guò)濾組件,然后是 2 號(hào),再然后是 4 號(hào),到達(dá)目標(biāo)處理邏輯后,再通過(guò) 3 號(hào)過(guò)濾組件,最終輸出結(jié)果。

輸入原始數(shù)據(jù)為 7,同樣是先經(jīng)過(guò) 1 號(hào)過(guò)濾組件,隨后是 2 號(hào),不過(guò)在 2 號(hào)中,直接返回了結(jié)果,這意味著過(guò)程被攔截,不再繼續(xù)向下傳遞數(shù)據(jù),至此結(jié)束并返回結(jié)果。

Laravel 框架中,原始數(shù)據(jù)是一個(gè) Request 對(duì)象,通過(guò)所定義的前置中間件,開發(fā)者可在中間件中獲取 Request 的信息,比如用戶的 Session/Cookie 以及 Header 等,驗(yàn)證數(shù)據(jù)是否完備等等,不完備或不符合要求的,則被攔截并返回一個(gè)響應(yīng)告知。若能正常通過(guò)則繼續(xù)傳遞至最終的處理邏輯,如控制器的某個(gè)方法或者一個(gè)匿名函數(shù)。通過(guò)這種模式,我們就實(shí)現(xiàn)了請(qǐng)求校驗(yàn)和業(yè)務(wù)邏輯的分離,而且這樣十分便于開發(fā)和維護(hù)。

實(shí)現(xiàn)

前面說(shuō)這么多,不知道讀者是否已經(jīng)有一套實(shí)現(xiàn)的思路了沒(méi)。

Pipeline 這個(gè)組件的功能十分明確,實(shí)現(xiàn)這種類似功能的肯定不少,選擇其作為代表分析,原因就是其實(shí)現(xiàn)的方式非常簡(jiǎn)潔、有力,不但其實(shí)現(xiàn)原理如此,面對(duì)開發(fā)人員,它的調(diào)用方式也十分清晰,利用匿名函數(shù)使得前置與后置的調(diào)用都很直觀,本文分析的重點(diǎn)就在這里。

實(shí)現(xiàn)的思路即使有了,在沒(méi)有很好地基礎(chǔ)之前,估計(jì)也很難去完成。當(dāng)然很多人愿意去閱讀其代碼,這樣就少走了不少?gòu)澛?,在這里,我的建議也是這樣。不過(guò),很多人看到源碼也很迷惑,因?yàn)橹虚g存在著非常多的回調(diào),只要基礎(chǔ)不夠扎實(shí),就很容易在期間產(chǎn)生諸多困惑。

不過(guò),逐步分析和對(duì)基礎(chǔ)知識(shí)的補(bǔ)完,就會(huì)發(fā)現(xiàn)再?gòu)?fù)雜的框架也不過(guò)是零碎的功能有序的構(gòu)建起來(lái)的。

array_reduce 的妙用
public function then(Closure $destination)
{
    $firstSlice = $this->getInitialSlice($destination);
    
    $callable = array_reduce(
        array_reverse($this->pipes), $this->getSlice(), $firstSlice
    );
    
    return $callable($this->passable);
}

上面的代碼就是 Pipeline 啟動(dòng)過(guò)程的起點(diǎn),當(dāng)然在調(diào)用 then 方法之前我們還有必要調(diào)用 sendthroughsend 是傳遞初始數(shù)據(jù),through 則是傳遞需要通過(guò)的中間件構(gòu)成的數(shù)組,沒(méi)必要贅述。

then 方法接受一個(gè)要求匿名函數(shù)的參數(shù),該參數(shù)所接受的匿名函數(shù),就是用于整個(gè)流程的邏輯處理部分的,數(shù)據(jù)穿過(guò)層層中間件,最終到達(dá)這里,所以該匿名函數(shù)可接受一個(gè)參數(shù),就是經(jīng)過(guò)過(guò)濾的數(shù)據(jù)啦。該方法囊括著所有功能,但是代碼不過(guò)幾行,因此肯定有額外的調(diào)度過(guò)程。

代碼中首先映入眼簾的就是 $this->getInitialSlice() ,該方法顧名思義,創(chuàng)建了一個(gè)初始化用的 Slice,這塊我們先不細(xì)說(shuō),因?yàn)殡S后就是本文的重點(diǎn),亦是組件實(shí)現(xiàn)的 核心功能array_reduce 函數(shù)!。

array_reduce 函數(shù)的作用文檔上寫的十分詳細(xì),可至官方中文文檔查閱:http://php.net/manual/zh/function.array-reduce.php

通過(guò)查閱文檔,我們可通過(guò)示例了解其作用本質(zhì)就是通過(guò)用戶自定義的方式去將一個(gè)數(shù)組合并成單一的一個(gè)值,因此該函數(shù)要求三個(gè)參數(shù):待合并的數(shù)組、用于合并邏輯的回調(diào)函數(shù)、初始合并的值(亦或者特殊情境下的最終值),用于合并邏輯的回調(diào)須接受兩個(gè)參數(shù)值,分別是上一次處理邏輯處理的結(jié)果(第一次不存在處理結(jié)果,則默認(rèn)為空,若設(shè)置了 array_reduce 的第三個(gè)參數(shù),則以該參數(shù)為初始值)和待處理的數(shù)組項(xiàng)。

Pipeline 組件恰到好處的使用了它。我們看得到,Pipeline 首先將我們用于處理的中間件數(shù)組通過(guò) array_reverse 取相反順序(至于為什么這么做后面你們就知道了),傳遞至 array_reduce 的第一個(gè)參數(shù)。第三個(gè)參數(shù)作為 array_reduce 認(rèn)定的默認(rèn)處理對(duì)象,Pipeline 用的是先前通過(guò) getInitalSlice 獲取到的(實(shí)際上是用戶傳進(jìn)來(lái)的目標(biāo)邏輯處理函數(shù))作為值傳遞。

然后就是本文第二個(gè)介紹的重點(diǎn),array_reduce 所接受的第二個(gè)參數(shù),通過(guò)調(diào)用 $this->getSlice() 獲取的一個(gè)匿名函數(shù)!

實(shí)現(xiàn)的核心

array_reduce 的第二個(gè)參數(shù)要求傳遞一個(gè)回調(diào)函數(shù)用于處理數(shù)組合并,$this->getSlice() 返回的正是這個(gè)處理函數(shù),我相信你們一定看到了 getSlice 返回的值,那么我就將這個(gè)匿名函數(shù)多帶帶拿出來(lái):

function ($stack, $pipe) {
    return function ($passable) use ($stack, $pipe) {
        if ($pipe instanceof Closure) {
            return call_user_func($pipe, $passable, $stack);
        }
        // 省略了一部分,該部分是針對(duì)中間件 “類” 而不是中間件匿名函數(shù)的,
        // 先前例子中我們用的都是以匿名函數(shù)作為數(shù)組傳遞進(jìn)來(lái)的,因此只會(huì)進(jìn)入上面那個(gè)條件,
        // 當(dāng)然 Laravel 框架中,傳遞進(jìn)來(lái)的則基本是中間件對(duì)象的類名,這段省略的代碼,
        // 和上面那個(gè) if 中的本質(zhì)的區(qū)別就是,省略的代碼中包含了中間件類的實(shí)例化過(guò)程并調(diào)用的是
        // 其 handle 方法而不是直接調(diào)用函數(shù),僅此~~
    };
};

我知道大家看到的代碼有很多行,但是實(shí)際上就只有一行 return function() { ... };,被執(zhí)行的也只有它。對(duì)于一些初學(xué)者,很容易產(chǎn)生一種錯(cuò)覺(jué):那個(gè)返回的 function 會(huì)在 return 前執(zhí)行。既然是錯(cuò)覺(jué),那就意味著不會(huì)被執(zhí)行,而是作為一個(gè)值被返回,可能會(huì)被后續(xù)某個(gè)地方所調(diào)用!可能會(huì)被后續(xù)某個(gè)地方所調(diào)用!可能會(huì)被后續(xù)某個(gè)地方所調(diào)用!這里只是個(gè)值!重要的事情說(shuō)三遍。

雖說(shuō)會(huì)被后面所調(diào)用,但我們依舊要在這里提一下這個(gè)被返回的匿名函數(shù),在這里,它又有著另一個(gè)名稱:閉包。閉包是由匿名函數(shù)(也成閉包函數(shù))構(gòu)成的一個(gè)整體,和普通的匿名函數(shù)有所不同,閉包中一定存在引用了外部數(shù)據(jù)并在內(nèi)部操作的情況。

這里需要注意,返回的不僅僅是個(gè)匿名函數(shù),更是一個(gè)閉包,該閉包中引用了兩個(gè)外部值,分別是 array_reduce 提供給第二參數(shù)中的回調(diào)的兩個(gè)參數(shù),即數(shù)組合并結(jié)果和當(dāng)前待合并的值。

第一次執(zhí)行時(shí),$stack 就是我們的目標(biāo)處理邏輯代碼段,$pipe 則是第一個(gè)中間件;

第二次執(zhí)行時(shí),$stack 是第一次執(zhí)行所返回的閉包,$pipe 則是第二個(gè)中間件,隨后以此類推。

最后一次執(zhí)行,返回的結(jié)果仍舊是一個(gè)閉包,該閉包中所引用的外部數(shù)據(jù)是倒數(shù)第二次的執(zhí)行返回的閉包,$pipe 是最后一個(gè)中間件。隨后,該閉包在 then 方法中被調(diào)用,傳遞進(jìn)了我們通過(guò) send 方法傳遞的值。

上面的描述可能異常抽象,我們讓其變得稍微直觀一些,我會(huì)將所有遍歷每一次執(zhí)行帶來(lái)的變化體現(xiàn)出來(lái)。不過(guò)為了方便理解,我需要改一下示例代碼,去掉中間的條件判斷,因?yàn)槲覀儸F(xiàn)在重點(diǎn)是理解這個(gè)流程而不是其功能,新的代碼與執(zhí)行結(jié)果如下:

send(0)->through($pipes)->then(function ($poster) {
    return $poster;
}); // 執(zhí)行輸出為 2

上述代碼,我們定義了三個(gè)中間件,同時(shí)我們的目標(biāo)邏輯代碼并沒(méi)做什么特殊的事情,這樣我們就可以專注在執(zhí)行流程上。下面便于分析,我做了一份偽代碼以及等式方便理解:

poster     = 0
f^0        = f(z)->{ z }                     // 定義目標(biāo)處理邏輯
f^1        = f(z, y)->{ f^y( z + 1 ) }       // 定義中間件 1
f^2        = f(z, y)->{ result = f^y(z); result - 1 }  // 定義中間件 2
f^3        = f(z, y)->{ f^y( z + 2 ) }       // 定義中間件 3
f^getSlice = f(y, x)->{
    f(z)->{
        call( f^x(z, y) )
    }
}

callback = array_reduce([f^3, f^2, f^1], f^getSlice, f^0);
callback(poster)

>>> 執(zhí)行上述過(guò)程

exec^1:
    // 第一次進(jìn)行 reduce,y 是目標(biāo)邏輯片段,x 是最后一個(gè)中間件,被閉包引用,
    // 閉包則作為合并結(jié)果返回,在此定義為 f^a。
    y   = f^0(z);
    x   = f^3;
    f^a = f(z)->{ call( f^x(z, y) ) }
exec^2:
    // 第二次進(jìn)行,y 是上次處理返回的閉包(即 f^a),x 是第二個(gè)中間件,再次生成閉包返回。
    y   = f^a;
    x   = f^2;
    f^b = f(z)->{ call( f^x(z, y) ) }
exec^3:
    // 第三次也是最后一次合并,同第二次。現(xiàn)在三個(gè)數(shù)組項(xiàng)被合并,
    // 合并結(jié)果為最后一次合并所返回的閉包。
    y   = f^b;
    x   = f^1;
    f^c = f(z)->{ call( f^x(z, y) ) }
exec^4:
    // 該閉包(最后一次合并結(jié)果)返回后,被調(diào)用,第一個(gè)參數(shù)為 z = poster = 1,開始執(zhí)行。
    // 該閉包的 z 參數(shù)即為 1,其余如 x、y 值見(jiàn) exec^3。
    call( f^c(0) ) = call( f^1(0, f^b) )
exec^5:
    // 繼續(xù)等式替換
    call( f^b(0 + 1) ) = call( f^2(0 + 1, f^a) )
exec^6:
    // 根據(jù)上已執(zhí)行過(guò)程返回結(jié)果,已執(zhí)行至中間件 2 的回調(diào),繼續(xù)等式替換
    result = f^a(0 + 1); result - 1
exec^7:
    result = call( f^3(0 + 1 , f^0) ); result - 1
exec^8:
    result = call( f^0(0 + 1 + 2) ); result - 1
exec^9:
    result = 3; result - 1

// 處理結(jié)果
result: 2
分析

根據(jù)偽代碼,和執(zhí)行過(guò)程,我們能了解到先前通過(guò) array_reverse 反序排列的中間件,由于在本文中,此處閉包逆向傳遞下去的特性(因?yàn)樗玫耐獠繀?shù)中,是前一執(zhí)行結(jié)果所返回的閉包),實(shí)際上依舊是按順序執(zhí)行的,我們?cè)谶@里也看到了如何利用該特性,實(shí)現(xiàn)前置和后置調(diào)用的原理以及攔截的原理。

前置調(diào)用時(shí),先處理自上傳遞下來(lái)的結(jié)果,隨后調(diào)用下一個(gè)(由中間件構(gòu)成的)閉包。后置調(diào)用時(shí),先調(diào)用下一個(gè)(有中間件構(gòu)成的)閉包,里面仍舊可能無(wú)數(shù)的引用,直到其中的目標(biāo)處理邏輯,最終返回結(jié)果,再處理。

攔截的原理就更簡(jiǎn)單了,由于攔截只存在于前置中間件,而前置中間件是先處理,然后調(diào)用傳遞進(jìn)來(lái)的閉包并返回其值,而若這個(gè)值不是來(lái)自于一個(gè)閉包調(diào)用的結(jié)果,就意味著肯定中間不存在調(diào)用關(guān)系,也就根本不會(huì)執(zhí)行到閉包中的下一個(gè)中間件。

總結(jié)

以上就是整個(gè) Pipeline 以及中間件的實(shí)現(xiàn),我知道很多人依舊十分糾結(jié),內(nèi)心充滿困惑。我仍舊建議老老實(shí)實(shí),從 array_reduce 這個(gè)函數(shù)的實(shí)際功能著手,然后把每一步執(zhí)行過(guò)程,寫下來(lái),慢慢的就明白了。這篇文章不僅僅只是 Laravel 組件的一個(gè)講解,更多是從中發(fā)現(xiàn) PHP 的一些基礎(chǔ)概念和知識(shí),要知道在強(qiáng)大的 PHP 框架也是用 PHP 寫出的,本質(zhì)上仍舊是在一個(gè)大的基礎(chǔ)上構(gòu)建的小世界而已。

所以作為一名 PHPer,永遠(yuǎn)不要忘了,你是在寫 PHP 的代碼。

我的博客地址:https://www.insp.top

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/30414.html

相關(guān)文章

  • Laravel Pipeline解讀

    摘要:大家好,今天給大家介紹下框架的。它是一個(gè)非常好用的組件,能夠使代碼的結(jié)構(gòu)非常清晰。的中間件機(jī)制便是基于它來(lái)實(shí)現(xiàn)的。通過(guò),生成一個(gè)接受一個(gè)參數(shù)的匿名函數(shù),然后執(zhí)行調(diào)用。不合法可以執(zhí)行初始化狀態(tài)的操作可以執(zhí)行保存狀態(tài)信息的操作執(zhí)行其它邏輯 大家好,今天給大家介紹下Laravel框架的Pipeline。它是一個(gè)非常好用的組件,能夠使代碼的結(jié)構(gòu)非常清晰。 Laravel的中間件機(jī)制便是基于它來(lái)...

    Neilyo 評(píng)論0 收藏0
  • Laravel 5.5 升級(jí)到 5.5.42 后遇到 Cookie 序列化問(wèn)題

    摘要:查閱官方文檔后得知,新版為了防止對(duì)象的序列化反序列化漏洞被利用,不再對(duì)值進(jìn)行自動(dòng)的序列化和反序列化處理。舉個(gè)栗子更新到后,因?yàn)椴辉僮詣?dòng)對(duì)值進(jìn)行序列化處理,而只能加密字符串?dāng)?shù)據(jù),這個(gè)時(shí)候程序就會(huì)拋出錯(cuò)誤。 最近手殘升級(jí)了項(xiàng)目里 Laravel 的小版本號(hào)(v5.5.39 => v5.5.45),這不升級(jí)則已,一升級(jí)就出了問(wèn)題! Sentry 平臺(tái)上提示錯(cuò)誤:openssl_encrypt...

    jollywing 評(píng)論0 收藏0
  • laravel使用中遇到問(wèn)題

    又有一段時(shí)間沒(méi)有學(xué)習(xí)了!迷茫,除了迷茫還是在迷茫!最近,公司接了一個(gè)laravel的項(xiàng)目,可惜沒(méi)有phper,于是開始學(xué)習(xí)laravel,現(xiàn)在的情況就是還沒(méi)學(xué)會(huì)走路就要開始跑了,所以遇到坑會(huì)摔得很痛! 安裝出現(xiàn)的問(wèn)題 安裝步驟(5.3.*) composer global require laravel/installer composer create...

    maybe_009 評(píng)論0 收藏0
  • php管道模式手測(cè)

    摘要:所以管道模式大致需要三個(gè)角色管道,閥門和載荷流水。模仿返回處理后的結(jié)果輸出結(jié)語(yǔ)上面的代碼并沒(méi)有達(dá)到中間件的真正執(zhí)行部分,例子中只是用到了管道模式的一部分。 一直在用Laravel框架,很喜歡laravel框架的中間件。在請(qǐng)求到結(jié)果之前,如果我們想要對(duì)路由或請(qǐng)求進(jìn)行額外的處理,簡(jiǎn)單的加個(gè)Midleware中間件就行了,很簡(jiǎn)單,很方便是不是。最近幾天看了下它的中間件的實(shí)現(xiàn)方式,把自己的心得...

    zzir 評(píng)論0 收藏0
  • Laravel核心解讀--中間件(Middleware)

    摘要:解析出后將進(jìn)入應(yīng)用的請(qǐng)求對(duì)象傳遞給的方法,在方法負(fù)責(zé)處理流入應(yīng)用的請(qǐng)求對(duì)象并返回響應(yīng)對(duì)象。攜帶了本次迭代的值。通過(guò)這種方式讓請(qǐng)求對(duì)象依次流過(guò)了要通過(guò)的中間件,達(dá)到目的地的方法。 中間件(Middleware)在Laravel中起著過(guò)濾進(jìn)入應(yīng)用的HTTP請(qǐng)求對(duì)象(Request)和完善離開應(yīng)用的HTTP響應(yīng)對(duì)象(Reponse)的作用, 而且可以通過(guò)應(yīng)用多個(gè)中間件來(lái)層層過(guò)濾請(qǐng)求、逐步完善...

    enda 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<