什么是中間件?
對于一個Web應(yīng)用來說,在一個請求真正處理前,我們可能會對請求做各種各樣的判斷,然后才可以讓它繼續(xù)傳遞到更深層次中。而如果我們用if else這樣子來,一旦需要判斷的條件越來越來,會使得代碼更加難以維護(hù),系統(tǒng)間的耦合會增加,而中間件就可以解決這個問題。我們可以把這些判斷獨立出來做成中間件,可以很方便的過濾請求。
我們都知道,使用Laravel中間件有三個步驟:
使用php artisan生成一個中間件,這里假設(shè)生成一個TestMiddleware的中間件
重寫TestMiddleware中的handle函數(shù),其中代碼邏輯寫在return $next($request);之前或者之后表示在執(zhí)行請求之前或者之后運(yùn)行這段代碼。
在app/Http/Kernel.php的routeMiddleware注冊一個中間件
然而,這上面的幾點下來,你會不會一頭霧水,為什么要執(zhí)行這么些操作之后才能使用一個中間件。尤其是第二點?
Laravel中的中間件
在Laravel中,中間件的實現(xiàn)其實是依賴于Illuminate\Pipeline\Pipeline這個類實現(xiàn)的,我們先來看看觸發(fā)中間件的代碼。很簡單,就是處理后把請求轉(zhuǎn)交給一個閉包就可以繼續(xù)傳遞了。
public function handle($request, Closure $next) {
//do something for $request
return $next($request);
}
中間件內(nèi)部實現(xiàn)
上面說道,中間件是靠Pipeline來實現(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)用了三個方法。再來看看這三個方法的代碼:
send方法
public function send($passable){
$this-》passable = $passable;
return $this;
}
其實send方法沒做什么事情,就是設(shè)置了需要在中間件中流水處理的對象,在這里就是HTTP請求實例。
through方法
public function through($pipes){
$this-》pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
through方法也很簡單,就是設(shè)置一下需要經(jīng)過哪些中間件處理。
then方法
真正難懂的來了,then方法代碼很簡潔,但是要理解可不容易。
public function then(Closure $destination){
//then方法接受一個閉包作為參數(shù),然后經(jīng)過getInitialSlice包裝,而getInitialSlice返回的其實也是一個閉包,如果還不知道什么是閉包先去看PHP文檔
$firstSlice = $this-》getInitialSlice($destination);
//反轉(zhuǎn)中間件數(shù)組,主要是利用了棧的特性,用處接下來再說
$pipes = array_reverse($this-》pipes);
//這個call_user_func先不要看,它其實就是執(zhí)行了一個array_reduce返回的閉包
return call_user_func(
//接下來用array_reduce來用回調(diào)函數(shù)處理數(shù)組,建議先去PHP文檔讀懂a(chǎn)rray_reduce的執(zhí)行原理。其實arrary_reduce什么事情都沒干,就是包裝閉包然后移交給call_user_func來執(zhí)行
array_reduce($pipes, $this-》getSlice(), $firstSlice), $this-》passable
?。?
}
然后就沒有然后了,這樣就過完了所有中間件,是不是很優(yōu)雅?
由于aray_reduce的第二個參數(shù)需要一個函數(shù),我們這里重點看看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));
}
};
};
}
看到可能會很頭暈,閉包返回閉包的。簡化一下就是getSlice()返回一個函數(shù)A,而函數(shù)A又返回了函數(shù)B。為什么要返回兩個函數(shù)呢?因為我們中間在傳遞過程中是用$next($request)來傳遞對象的,而$next($request)這樣的寫法就表示是執(zhí)行了這個閉包,這個閉包就是函數(shù)A,然后返回函數(shù)B,可以給下一個中間件繼續(xù)傳遞。
?
評論