本文實(shí)例講述了Laravel的中間件實(shí)現(xiàn)原理。分享給大家供大家參考,具體如下:
#1 什么是中間件?
對(duì)于一個(gè)Web應(yīng)用來說,在一個(gè)請(qǐng)求真正處理前,我們可能會(huì)對(duì)請(qǐng)求做各種各樣的判斷,然后才可以讓它繼續(xù)傳遞到更深層次中。而如果我們用if else這樣子來,一旦需要判斷的條件越來越來,會(huì)使得代碼更加難以維護(hù),系統(tǒng)間的耦合會(huì)增加,而中間件就可以解決這個(gè)問題。我們可以把這些判斷獨(dú)立出來做成中間件,可以很方便的過濾請(qǐng)求。
#2 Laravel中的中間件
在Laravel中,中間件的實(shí)現(xiàn)其實(shí)是依賴于Illuminate\Pipeline\Pipeline這個(gè)類實(shí)現(xiàn)的,我們先來看看觸發(fā)中間件的代碼。很簡(jiǎn)單,就是處理后把請(qǐng)求轉(zhuǎn)交給一個(gè)閉包就可以繼續(xù)傳遞了。
public function handle($request, Closure $next) { //do something for $request return $next($request); }
#3 中間件內(nèi)部實(shí)現(xiàn)
上面說道,中間件是靠Pipeline來實(shí)現(xiàn)的,它的調(diào)用在Illuminate\Routing\Router中
return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run($request) ); });
可以看到,中間件執(zhí)行過程調(diào)用了三個(gè)方法。再來看看這三個(gè)方法的代碼:
send方法
public function send($passable){ $this->passable = $passable; return $this; }
其實(shí)send方法沒做什么事情,就是設(shè)置了需要在中間件中流水處理的對(duì)象,在這里就是HTTP請(qǐng)求實(shí)例。
through方法
public function through($pipes){ $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; }
through方法也很簡(jiǎn)單,就是設(shè)置一下需要經(jīng)過哪些中間件處理。
then方法
真正難懂的來了,then方法代碼很簡(jiǎn)潔,但是要理解可不容易。
public function then(Closure $destination){ //then方法接受一個(gè)閉包作為參數(shù),然后經(jīng)過getInitialSlice包裝,而getInitialSlice返回的其實(shí)也是一個(gè)閉包,如果還不知道什么是閉包先去看PHP文檔 $firstSlice = $this->getInitialSlice($destination); //反轉(zhuǎn)中間件數(shù)組,主要是利用了棧的特性,用處接下來再說 $pipes = array_reverse($this->pipes); //這個(gè)call_user_func先不要看,它其實(shí)就是執(zhí)行了一個(gè)array_reduce返回的閉包 return call_user_func( //接下來用array_reduce來用回調(diào)函數(shù)處理數(shù)組,建議先去PHP文檔讀懂a(chǎn)rray_reduce的執(zhí)行原理。其實(shí)arrary_reduce什么事情都沒干,就是包裝閉包然后移交給call_user_func來執(zhí)行 array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable ); }
然后就沒有然后了,這樣就過完了所有中間件,是不是很優(yōu)雅?
由于aray_reduce的第二個(gè)參數(shù)需要一個(gè)函數(shù),我們這里重點(diǎn)看看getSlice()方法的源碼
protected function getSlice(){ return function ($stack, $pipe) { //這里$stack return function ($passable) use ($stack, $pipe) { 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)); } }; }; }
看到可能會(huì)很頭暈,閉包返回閉包的。簡(jiǎn)化一下就是getSlice()返回一個(gè)函數(shù)A,而函數(shù)A又返回了函數(shù)B。為什么要返回兩個(gè)函數(shù)呢?因?yàn)槲覀冎虚g在傳遞過程中是用$next($request)來傳遞對(duì)象的,而$next($request)這樣的寫法就表示是執(zhí)行了這個(gè)閉包,這個(gè)閉包就是函數(shù)A,然后返回函數(shù)B,可以給下一個(gè)中間件繼續(xù)傳遞。
再來簡(jiǎn)化一下代碼就是:
//這里的$stack其實(shí)就是閉包,第一次遍歷的時(shí)候會(huì)傳入$firstSlice這個(gè)閉包,以后每次都會(huì)傳入下面的那個(gè)function; 而$pipe就是每一個(gè)中間件 array_reduce($pipes, function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { }; }, $firstSlice);
再來看這一段代碼:
//判斷是否為閉包,這里就是判斷中間件形式是不是閉包,是的話直接執(zhí)行并且傳入$passable[請(qǐng)求實(shí)例]和$stack[傳遞給下一個(gè)中間件的閉包],并且返回 if ($pipe instanceof Closure) { return call_user_func($pipe, $passable, $stack); //不是閉包的時(shí)候就是形如這樣Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode執(zhí)行 } else { //解析,把名稱返回,這個(gè)$parameters看了許久源碼還是看不懂,應(yīng)該是和參數(shù)相關(guān),不過不影響我們的分析 list($name, $parameters) = $this->parsePipeString($pipe); //從容器中解析出中間件實(shí)例并且執(zhí)行handle方法 return call_user_func_array([$this->container->make($name), $this->method], //$passable就是請(qǐng)求實(shí)例,而$stack就是傳遞的閉包 array_merge([$passable, $stack], $parameters)); }
再看一張圖片:
每一次迭代傳入上一次的閉包和需要執(zhí)行的中間件,由于反轉(zhuǎn)了數(shù)組,基于棧先進(jìn)后出的特性,所以中間件3第一個(gè)被包裝,中間件1就在最外層了。要記得,arrary_reduce他不執(zhí)行中間件代碼,而是包裝中間件。
看到這里應(yīng)該明白了,array_reduce最后會(huì)返回func3,那么call_user_func(func3,$this->passable)實(shí)際就是
return call_user_func($middleware[0]->handle, $this->passable, func2);
而我們的中間件中的handle代碼是:
public function handle($request, Closure $next) { return $next($request); }
這里就相當(dāng)于return func2($request),這里的$request就是經(jīng)過上一個(gè)中間件處理過的。所以正果中間件的過程就完了,理解起來會(huì)有點(diǎn)繞,只要記得最后是由最外面的call_user_func來執(zhí)行中間件代碼的
希望本文所述對(duì)大家基于Laravel框架的PHP程序設(shè)計(jì)有所幫助。
更多Laravel中間件實(shí)現(xiàn)原理詳解相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!

Alat AI Hot

Undress AI Tool
Gambar buka pakaian secara percuma

Undresser.AI Undress
Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover
Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Clothoff.io
Penyingkiran pakaian AI

Video Face Swap
Tukar muka dalam mana-mana video dengan mudah menggunakan alat tukar muka AI percuma kami!

Artikel Panas

Alat panas

Notepad++7.3.1
Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina
Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1
Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6
Alat pembangunan web visual

SublimeText3 versi Mac
Perisian penyuntingan kod peringkat Tuhan (SublimeText3)