首頁  >  文章  >  後端開發  >  探究Laravel的中間件是如何達成的

探究Laravel的中間件是如何達成的

*文
*文原創
2018-01-02 14:05:021899瀏覽

Laravel的中間件是如何實現的?本文主要介紹了Laravel中間件實現原理,較為詳細的分析了Laravel中間件的概念、原理及相關方法與使用技巧,需要的朋友可以參考下。希望對大家有幫助。

具體如下:

#1 什麼是中間件?

對於一個網路應用程式來說,在一個請求真正處理之前,我們可能會對請求做各種各樣的判斷,然後才可以讓它繼續傳遞到更深層次。而如果我們用if else這樣子來,一旦需要判斷的條件越來越來,會使得程式碼更難以維護,系統間的耦合會增加,而中間件就可以解決這個問題。我們可以把這些判斷獨立出來做成中間件,可以很方便的過濾請求。

#2 Laravel中的中間件

在Laravel中,中間件的實作其實是依賴Illuminate\Pipeline\Pipeline這個類別實現的,我們先來看看觸發中間件的程式碼。很簡單,就是處理完後把請求轉交給閉包就可以繼續傳遞了。

public function handle($request, Closure $next) {
  //do something for $request
  return $next($request);
}

#3 中間件​​內部實作

上面說道,中間件是靠Pipeline來實現的,它的呼叫在Illuminate\Routing\Router中

return (new Pipeline($this->container))
            ->send($request)
            ->through($middleware)
            ->then(function ($request) use ($route) {
              return $this->prepareResponse(
                $request,
                $route->run($request)
              );
            });

可以看到,中間件執行過程呼叫了三個方法。再來看看這三個方法的程式碼:

send方法

public function send($passable){
  $this->passable = $passable;
  return $this;
}

其實send方法沒做什麼事情,就是設定了需要在中間件中流水處理的對象,在這裡就是HTTP請求實例。

through方法

public function through($pipes){
  $this->pipes = is_array($pipes) ? $pipes : func_get_args();
  return $this;
}

through方法也很簡單,就是設定一下需要經過哪些中間件處理。

then方法

真正難懂的來了,then方法程式碼很簡潔,但是要理解可不容易。

public function then(Closure $destination){
  //then方法接受一个闭包作为参数,然后经过getInitialSlice包装,而getInitialSlice返回的其实也是一个闭包,如果还不知道什么是闭包先去看PHP文档
  $firstSlice = $this->getInitialSlice($destination);
  //反转中间件数组,主要是利用了栈的特性,用处接下来再说
  $pipes = array_reverse($this->pipes);
  //这个call_user_func先不要看,它其实就是执行了一个array_reduce返回的闭包
  return call_user_func(  
    //接下来用array_reduce来用回调函数处理数组,建议先去PHP文档读懂array_reduce的执行原理。其实arrary_reduce什么事情都没干,就是包装闭包然后移交给call_user_func来执行
    array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
  );
}

然後就沒有然後了,這樣就過完了所有中間件,是不是很優雅?

由於aray_reduce的第二個參數需要一個函數,我們在這裡重點看看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()回傳一個函數A,而函數A又回傳了函數B。為什麼要回傳兩個函數呢?因為我們中間在傳遞過程中是用$next($request)來傳遞物件的,而$nex​​t($request)這樣的寫法就表示是執行了這個閉包,這個閉包就是函數A,然後回傳函數B ,可以給下一個中間件繼續傳遞。

再來簡化程式碼就是:

//这里的$stack其实就是闭包,第一次遍历的时候会传入$firstSlice这个闭包,以后每次都会传入下面的那个function; 而$pipe就是每一个中间件
array_reduce($pipes, function ($stack, $pipe) {  
  return function ($passable) use ($stack, $pipe) {
  };
}, $firstSlice);

再來看這一段程式碼:

//判断是否为闭包,这里就是判断中间件形式是不是闭包,是的话直接执行并且传入$passable[请求实例]和$stack[传递给下一个中间件的闭包],并且返回
if ($pipe instanceof Closure) {  
  return call_user_func($pipe, $passable, $stack);
//不是闭包的时候就是形如这样Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode执行
} else {  
  //解析,把名称返回,这个$parameters看了许久源码还是看不懂,应该是和参数相关,不过不影响我们的分析
  list($name, $parameters) = $this->parsePipeString($pipe); 
  //从容器中解析出中间件实例并且执行handle方法
  return call_user_func_array([$this->container->make($name), $this->method], 
  //$passable就是请求实例,而$stack就是传递的闭包
  array_merge([$passable, $stack], $parameters));  
}

再看一張圖片:

每一次迭代傳入上一次的閉包和需要執行的中間件,由於反轉了數組,基於棧先進後出的特性,所以中間件3第一個被包裝,中間件1就在最外層了。要記得,arrary_reduce他不執行中間件程式碼,而是包裝中間件。

看到這裡應該明白了,array_reduce最後會回傳func3,那麼call_user_func(func3,$this->passable)實際上是

return call_user_func($middleware[0]-> handle, $this->passable, func2);

而我們的中間件中的handle代碼是:

public function handle($request, Closure $next) {
  return $next($request);
}

這裡就相當於return func2($request),這裡的$ request就是經過上一個中介軟體處理過的。所以正果中間件的過程就完了,理解起來會有點繞,只要記得最後是由最外面的call_user_func來執行中間件代碼的

##相關推薦:

#Laravel優化分割路由檔案

#Laravel使用Pagination外掛程式實作自訂分頁

################################################# #######laravel寫APP介面(API)##########

以上是探究Laravel的中間件是如何達成的的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn