protected function sendRequestThroughRouter($request) { # $this->app->instance('request', $request); # Facade::clearResolvedInstance('request'); # $this->bootstrap(); return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); // $this->dispatchToRouter(),後期繼續 } new \Illuminate\Routing\Pipeline($this->app): public function __construct(Container $container = null) { $this->container = $container; } public function send($passable) { $this->passable = $passable; return $this; } public function through($pipes) { #################################################################################### # $this->middleware = [ # # \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, # # \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, # # \App\Http\Middleware\TrimStrings::class, # # \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, # # ]; # #################################################################################### $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } // 中間件的本質 public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); // 上面操做完以後返回一個匿名函數,接受一個參數 return $pipeline($this->passable); } // 注意這個是子類裏面會調用的父carry,區別在於子類加入了異常處理 protected function carry() { // 接受匿名函數$stack參數和$pipe參數,返回匿名函數,再將此匿名函數做爲第一個參數$stack迭代傳入。 return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } // 中間件做用是提供了一種方便的機制來過濾進入應用的 HTTP 請求 // $this->method默認爲handle,可經過via方法進行設置 return $pipe->{$this->method}(...$parameters); }; }; } protected function parsePipeString($pipe) { // 不帶參數的pipe(class)或帶參數(參數以,分割)的pipe(class:param1,param2,param3...) list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []); if (is_string($parameters)) { $parameters = explode(',', $parameters); } // $parameters爲[]數組或[param1,param2,param3...] return [$name, $parameters]; } protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; }
前置條件bootstrap
array_reduce接受三個參數,第一個參數接收數組,第二個參數函數名(也能夠是匿名函數,函數有兩個參數,分別表明$result和$item),第三個參數(可選),該參數將被當成是數組中的第一個值來處理,或者若是數組爲空的話就做爲最終返回值。數組
匿名函數也叫閉包函數(closures),容許臨時建立一個沒有指定名稱的函數,經過 Closure 類來實現的。閉包
當對閉包函數進行賦值時,PHP 便會自動將此種表達式轉換成內置類 Closure 的對象對象在進行賦值。app
當閉包函數使用到 use 使用外部數據時,會在 Closure 對象生成一個 static 屬性數組進行存放。函數
當閉包函數使用到參數時,會在 Closure 對象生成一個 parameter 屬性數組進行存放。this
public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); return $pipeline($this->passable); }
生成最終匿名函數的過程:spa
array_reduce執行第一次時獲得以下簡化的匿名函數返回,將會繼續做爲第一個參數進行迭代: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull" } } 第二次: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull" } } ["pipe"]=> string(15) "App\Http\Middleware\TrimStrings" } } 第三次: object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#id (1) { ["static"]=> array(2) { ["stack"]=> object(Closure)#1 (0) { // $this->prepareDestination($destination) } ["pipe"]=> string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull" } } ["pipe"]=> string(15) "App\Http\Middleware\TrimStrings" } } ["pipe"]=> string(15) "Illuminate\Foundation\Http\Middleware\ValidatePostSize" } }
依次類推,最終獲得一個匿名函數以下(接受一個參數,此匿名函數內部使用上面遞歸形式的$pipe和$stack)。code
function ($passable) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); // 實例化middleware $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } return $pipe->{$this->method}(...$parameters); // 語法糖模式,由於middleware參數無關緊要 };
最終匿名函數的調用過程(從最外層開始,這就是前面爲何要array_reverse,一層一層往裏撥,總體上的處理是:$pipe如果匿名函數,直接調用。如果字符串,則解析成對應的類和參數,make類,再組成參數數組。最後調用$pipe->handle)router
最外層($pipe=Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode): (new \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode)->handle($passable, $stack) 執行完前置操做後,調用$stack($passable),繼續進行下一層 下一層($pipe=Illuminate\Foundation\Http\Middleware\ValidatePostSize): (new \Illuminate\Foundation\Http\Middleware\ValidatePostSize)->handle($passable, $stack) 執行完前置操做後,調用$stack($passable),繼續進行下一層 **以此類推,當每層中間件的前置任務所有完成,即遞歸執行到最裏面一層(路由分發,解析請求,返回響 應),再由最內層一層一層往回走,執行每層中間件的後置任務。至此,返回本次請求的響應。**
最內層(即路由分發,解析請求,返回響應)的操做代碼展現,後續分析:即執行$destination($passable)匿名函數,位於下面方法中。中間件
protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } $destination: protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }
說明: 全部的後置操做,都是執行到最內層,遞歸往回走時纔會執行逐層執行。