Laravel中間件實(shí)現(xiàn)原理詳解
本文實(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í)際就是
而我們的中間件中的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í)行中間件代碼的
更多關(guān)于Laravel相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Laravel框架入門與進(jìn)階教程》、《php優(yōu)秀開發(fā)框架總結(jié)》、《smarty模板入門基礎(chǔ)教程》、《php日期與時(shí)間用法總結(jié)》、《php面向?qū)ο蟪绦蛟O(shè)計(jì)入門教程》、《php字符串(string)用法總結(jié)》、《php+mysql數(shù)據(jù)庫操作入門教程》及《php常見數(shù)據(jù)庫操作技巧匯總》
希望本文所述對(duì)大家基于Laravel框架的PHP程序設(shè)計(jì)有所幫助。
- Laravel5.1框架路由分組用法實(shí)例分析
- laravel框架分組控制器和分組路由實(shí)現(xiàn)方法示例
- laravel中命名路由的使用方法
- Laravel 5框架學(xué)習(xí)之路由、控制器和視圖簡(jiǎn)介
- Laravel框架實(shí)現(xiàn)利用中間件進(jìn)行操作日志記錄功能
- Laravel獲取當(dāng)前請(qǐng)求的控制器和方法以及中間件的例子
- Laravel 6 將新增為指定隊(duì)列任務(wù)設(shè)置中間件的功能
- 淺談Laravel中的三種中間件的作用
- Laravel 4 初級(jí)教程之視圖、命名空間、路由
- 解決Laravel自定義類引入和命名空間的問題
- 修改Laravel自帶的認(rèn)證系統(tǒng)的User類的命名空間的步驟
- laravel框架路由分組,中間件,命名空間,子域名,路由前綴實(shí)例分析
相關(guān)文章
php獲取數(shù)據(jù)庫結(jié)果集方法(推薦)
下面小編就為大家?guī)硪黄猵hp獲取數(shù)據(jù)庫結(jié)果集方法(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-0664位windows系統(tǒng)下安裝Memcache緩存
這篇文章主要介紹了64位windows系統(tǒng)下安裝Memcache緩存的相關(guān)資料,需要的朋友可以參考下2015-12-12深入mysql_fetch_row()與mysql_fetch_array()的區(qū)別詳解
本篇文章是對(duì)mysql_fetch_row()與mysql_fetch_array()的區(qū)別進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06Yii安裝EClientScript插件擴(kuò)展實(shí)現(xiàn)css,js文件代碼壓縮合并加載功能
這篇文章主要介紹了Yii安裝EClientScript插件擴(kuò)展實(shí)現(xiàn)css,js文件代碼壓縮合并加載功能,分析了EClientScript插件的下載、安裝、設(shè)置及使用的相關(guān)技巧,需要的朋友可以參考下2016-07-07Laravel登錄失敗次數(shù)限制的實(shí)現(xiàn)方法
這篇文章主要給大家介紹了關(guān)于Laravel登錄失敗次數(shù)限制的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08Laravel5.1 框架模型工廠ModelFactory用法實(shí)例分析
這篇文章主要介紹了Laravel5.1 框架模型工廠ModelFactory用法,結(jié)合實(shí)例形式分析了laravel5.1框架模型工廠ModelFactory基本功能、定義與使用方法,需要的朋友可以參考下2020-01-01