摘要:管道流原理強(qiáng)烈依賴函數(shù),我們先來了解下函數(shù)的使用。第二次迭代時(shí),的值為上述返回的閉包偽代碼,的值為,返回一個(gè)閉包,當(dāng)我們執(zhí)行這個(gè)閉包時(shí),滿足,得到結(jié)果。自定義中間件為的管道流核心類在的方法中,為上述的閉包,為要通過的中間件數(shù)組,為對(duì)象。
Laravel管道流原理強(qiáng)烈依賴array_reduce函數(shù),我們先來了解下array_reduce函數(shù)的使用。
原標(biāo)題PHP 內(nèi)置函數(shù) array_reduce 在 Laravel 中的使用
array_reduce在看array_reduce在laravel中的應(yīng)用時(shí),先來看看array_reduce官方文檔是怎么說的。
array_reduce() 將回調(diào)函數(shù) callback 迭代地作用到 array 數(shù)組中的每一個(gè)單元中,從而將數(shù)組簡化為單一的值。
mixed array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] )
array
輸入的 array。
callback
mixed callback ( mixed $carry , mixed $item )
$carry包括上次迭代的值,如果本次迭代是第一次,那么這個(gè)值是 initial,item 攜帶了本次迭代的值
initial
如果指定了可選參數(shù) initial,該參數(shù)將在處理開始前使用,或者當(dāng)處理結(jié)束,數(shù)組為空時(shí)的最后一個(gè)結(jié)果。
從文檔說明可以看出,array_reduce函數(shù)是把數(shù)組的每一項(xiàng),都通過給定的callback函數(shù),來簡化的。
那我們就來看看是怎么簡化的。
$arr = ["AAAA", "BBBB", "CCCC"]; $res = array_reduce($arr, function($carry, $item){ return $carry . $item; });
給定的數(shù)組長度為3,故總迭代三次。
第一次迭代時(shí) $carry = null $item = AAAA 返回AAAA
第一次迭代時(shí) $carry = AAAA $item = BBBB 返回AAAABBBB
第一次迭代時(shí) $carry = AAAABBBB $item = CCCC 返回AAAABBBBCCCC
帶初始值的情況這種方式將數(shù)組簡化為一串字符串AAAABBBBCCCC
$arr = ["AAAA", "BBBB", "CCCC"]; $res = array_reduce($arr, function($carry, $item){ return $carry . $item; }, "INITIAL-");
第一次迭代時(shí)($carry = INITIAL-),($item = AAAA) 返回INITIAL-AAAA
第一次迭代時(shí)($carry = INITIAL-AAAA),($item = BBBB), 返回INITIAL-AAAABBBB
第一次迭代時(shí)($carry = INITIAL-AAAABBBB),($item = CCCC),返回INITIAL-AAAABBBBCCCC
閉包這種方式將數(shù)組簡化為一串字符串INITIAL-AAAABBBBCCCC
$arr = ["AAAA", "BBBB", "CCCC"]; //沒帶初始值 $res = array_reduce($arr, function($carry, $item){ return function() use ($item){//這里只use了item return strtolower($item) . "-"; }; });
第一次迭代時(shí),$carry:null,$item = AAAA,返回一個(gè)use了$item = AAAA的閉包
第二次迭代時(shí),$carry:use了$item = AAAA的閉包,$item = BBBB,返回一個(gè)use了$item = BBBB的閉包
第一次迭代時(shí),$carry:use了$item = BBBB的閉包,$item = CCCC,返回一個(gè)use了$item = CCCC的閉包
這種方式將數(shù)組簡化為一個(gè)閉包,即最后返回的閉包,當(dāng)我們執(zhí)行這個(gè)閉包時(shí)$res()得到返回值CCCC-
上面這種方式只use ($item),每次迭代返回的閉包在下次迭代時(shí),我們都沒有用起來。只是又重新返回了一個(gè)use了當(dāng)前item值的閉包。
閉包USE閉包$arr = ["AAAA"]; $res = array_reduce($arr, function($carry, $item){ return function () use ($carry, $item) { if (is_null($carry)) { return "Carry IS NULL" . $item; } }; });
注意,此時(shí)的數(shù)組長度為1,并且沒有指定初始值
由于數(shù)組長度為1,故只迭代一次,返回一個(gè)閉包 use($carry = null, $item = "AAAA"),當(dāng)我們執(zhí)行($res())這個(gè)閉包時(shí),得到的結(jié)果為Carry IS NULLAAAA。
接下來我們重新改造下,
$arr = ["AAAA", "BBBB"]; $res = array_reduce($arr, function($carry, $item){ return function () use ($carry, $item) { if (is_null($carry)) { return "Carry IS NULL" . $item; } if ($carry instanceof Closure) { return $carry() . $item; } }; });
我們新增了一個(gè)條件判斷,若當(dāng)前迭代的值是一個(gè)閉包,返回該閉包的執(zhí)行結(jié)果。
第一次迭代時(shí),$carry的值為null,$item的值為AAAA,返回一個(gè)閉包,
//偽代碼 function () use ($carry = null, $item = AAAA) { if (is_null($carry)) { return "Carry IS NULL" . $item; } if ($carry instanceof Closure) { return $carry() . $item; } }
假設(shè)我們直接執(zhí)行該閉包,將會(huì)返回Carry IS NULLAAAA的結(jié)果。
第二次迭代時(shí),$carry的值為上述返回的閉包(偽代碼),$item的值為BBBB,返回一個(gè)閉包,
Laravel中的array_reverse當(dāng)我們執(zhí)行這個(gè)閉包時(shí),滿足$carry instanceof Closure,得到結(jié)果Carry IS NULLAAAABBBB。
大致了解了array_reverse函數(shù)的使用后,我們來瞅瞅laravel管道流里使用array_reverse的情況。
我在Laravel中間件原理中有闡述,強(qiáng)烈建議先去看看Laravel中間件原理再回過頭來接著看。
php內(nèi)置方法array_reduce把所有要通過的中間件都通過callback方法并壓縮為一個(gè)Closure。最后在執(zhí)行Initial
Laravel中通過全局中間件的核心代碼如下:
//IlluminateFoundationHttpKernel.php protected function sendRequestThroughRouter($request) { return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } protected function dispatchToRouter() { return function ($request) { $this->app->instance("request", $request); return $this->router->dispatch($request); }; }
正如我前面說的,我們發(fā)送一個(gè)$request對(duì)象通過middleware中間件數(shù)組,最后在執(zhí)行dispatchToRouter方法。
假設(shè)有兩個(gè)全局中間件,我們來看看這兩個(gè)中間件是如何通過管道壓縮為一個(gè)Closure的。
IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class, AppHttpMiddlewareAllowOrigin::class,//自定義中間件
IlluminatePipelinePipeline為laravel的管道流核心類.
在IlluminatePipelinePipeline的then方法中,$destination為上述的dispatchToRouter閉包,pipes為要通過的中間件數(shù)組,passable為Request對(duì)象。
public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); return $pipeline($this->passable); }
array_reverse函數(shù)將中間件數(shù)組的每一項(xiàng)都通過$this->carry(),初始值為上述dispatchToRouter方法返回的閉包。
protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } protected function carry() { return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { //解析中間件參數(shù) list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } return $pipe->{$this->method}(...$parameters); }; }; }
第一次迭代時(shí),返回一個(gè)閉包,use了$stack和$pipe,$stack的值為初始值閉包,$pipe為中間件類名,此處是AppHttpMiddlewareAllowOrigin::class(注意array_reverse函數(shù)把傳進(jìn)來的中間件數(shù)組倒敘了)。
假設(shè)我們直接運(yùn)行該閉包,由于此時(shí)$pipe是一個(gè)String類型的中間件類名,只滿足! is_object($pipe)這個(gè)條件,我們將直接從容器中make一個(gè)該中間件的實(shí)列出來,在執(zhí)行該中間件實(shí)列的handle方法(默認(rèn)$this->method為handle)。并且將request對(duì)象和初始值作為參數(shù),傳給這個(gè)中間件。
public function handle($request, Closure $next) { //...... }
在這個(gè)中間件的handle方法中,當(dāng)我們直接執(zhí)行return $next($request)時(shí),相當(dāng)于我們開始執(zhí)行array_reduce函數(shù)的初始值閉包了,即上述的dispatchToRouter方法返回的閉包。
protected function dispatchToRouter() { return function ($request) { $this->app->instance("request", $request); return $this->router->dispatch($request); }; }
好,假設(shè)結(jié)束。在第二次迭代時(shí),也返回一個(gè)use了$stack和$pipe,$stack的值為我們第一次迭代時(shí)返回的閉包,$pipe為中間件類名,此處是IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class。
兩次迭代結(jié)束,回到then方法中,我們手動(dòng)執(zhí)行了第二次迭代返回的閉包。
return $pipeline($this->passable);
當(dāng)執(zhí)行第二次迭代返回的閉包時(shí),當(dāng)前閉包use的$pipe為IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,同樣只滿足! is_object($pipe)這個(gè)條件,我們將會(huì)從容器中make出CheckForMaintenanceMode中間件的實(shí)列,在執(zhí)行該實(shí)列的handle方法,并且把第一次迭代返回的閉包作為參數(shù)傳到handle方法中。
當(dāng)我們?cè)?b>CheckForMaintenanceMode中間件的handle方法中執(zhí)行return $next($request)時(shí),此時(shí)的$next為我們第一次迭代返回的閉包,將回到我們剛才假設(shè)的流程那樣。從容器中make一個(gè)AppHttpMiddlewareAllowOrigin實(shí)列,在執(zhí)行該實(shí)列的handle方法,并把初始值閉包作為參數(shù)傳到AllowOrigin中間件的handle方法中。當(dāng)我們?cè)僭?b>AllowOrigin中間件中執(zhí)行return $next($request)時(shí),代表我們所有中間件都通過完成了,接下來開始執(zhí)行dispatchToRouter。
中間件是區(qū)分先后順序的,從這里你應(yīng)該能明白為什么要把中間件用array_reverse倒敘了。
并不是所有中間件在運(yùn)行前都已經(jīng)實(shí)例化了的,用到的時(shí)候才去想容器取
中間件不執(zhí)行$next($request)后續(xù)所有中間件無法執(zhí)行。
這篇文章是專們?yōu)榱松弦黄狶aravel中間件原理寫的,因?yàn)樵趯慙aravel中間件原理時(shí)我也不很清楚array_reduce在laravel中的運(yùn)行流程。如果有什么不對(duì)的,歡迎指正。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/23190.html
摘要:直到所有中間件都執(zhí)行完畢,最后在執(zhí)行最后的即上述的方法如果上述有地方難懂的,可以參考這邊文章內(nèi)置函數(shù)在中的使用以上是在通過全局中間件時(shí)的大致流程,通過中間件和路由中間件也是一樣的,都是采用管道流操作,詳情可翻閱源碼 簡介 Laravel 中間件提供了一種方便的機(jī)制來過濾進(jìn)入應(yīng)用的 HTTP 請(qǐng)求, 如ValidatePostSize用來驗(yàn)證POST請(qǐng)求體大小、ThrottleReque...
摘要:將請(qǐng)求傳入到指定的中間件路由。用于處理任務(wù)的方法接收兩個(gè)參數(shù),第一個(gè)是一個(gè)可傳遞的對(duì)象,第二個(gè)是閉包,在運(yùn)行最后一個(gè)管道后對(duì)象將被重定向到這個(gè)閉包。我希望這個(gè)實(shí)例能夠讓你對(duì)有更深如的了解,并知道如何使用它們。 這是一篇譯文,原文 Understanding Laravel Pipelines。譯文首發(fā)于 深入理解 Laravel 管道,轉(zhuǎn)載請(qǐng)注明出處。 基本上,你可以使用 larave...
摘要:好久沒有寫文章了,記錄一下這段時(shí)間學(xué)習(xí)的東西吧中間件是個(gè)非常方便的東西,能將一些邏輯實(shí)現(xiàn)解耦,并且在中,中間件的編寫也是非常的方便。對(duì)于的中間件,他的實(shí)現(xiàn)原理也是和這個(gè)一樣的。 好久沒有寫文章了,記錄一下這段時(shí)間學(xué)習(xí)的東西吧 laravel中間件是個(gè)非常方便的東西,能將一些邏輯實(shí)現(xiàn)解耦,并且在laravel中, 中間件的編寫也是非常的方便。誰用誰知道。 1.裝飾器模式 laravel中...
摘要:官方地址是目前最流行的框架,發(fā)展勢(shì)頭迅猛,應(yīng)用非常廣泛,有豐富的擴(kuò)展包可以應(yīng)付你能想到的各種應(yīng)用場(chǎng)景,框架思想前衛(wèi),跟隨時(shí)代潮流,提倡優(yōu)雅代碼,自稱為工匠,其中的模板引擎容器以及擴(kuò)展包為業(yè)務(wù)的開發(fā)提供了極大的便利。 laravel5.5+ laravel官方地址 laravel是目前最流行的php框架,發(fā)展勢(shì)頭迅猛,應(yīng)用非常廣泛,有豐富的擴(kuò)展包可以應(yīng)付你能想到的各種應(yīng)用場(chǎng)景,lara...
摘要:所以管道模式大致需要三個(gè)角色管道,閥門和載荷流水。模仿返回處理后的結(jié)果輸出結(jié)語上面的代碼并沒有達(dá)到中間件的真正執(zhí)行部分,例子中只是用到了管道模式的一部分。 一直在用Laravel框架,很喜歡laravel框架的中間件。在請(qǐng)求到結(jié)果之前,如果我們想要對(duì)路由或請(qǐng)求進(jìn)行額外的處理,簡單的加個(gè)Midleware中間件就行了,很簡單,很方便是不是。最近幾天看了下它的中間件的實(shí)現(xiàn)方式,把自己的心得...
閱讀 728·2021-08-17 10:11
閱讀 1594·2019-08-30 11:15
閱讀 1017·2019-08-26 13:54
閱讀 3502·2019-08-26 11:47
閱讀 1212·2019-08-26 10:20
閱讀 2816·2019-08-23 18:35
閱讀 1213·2019-08-23 17:52
閱讀 1297·2019-08-23 16:19