<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                [TOC] # 說明 ThinkPHP 6.0 對中間件的實現進行了巨大的改進,比起之前版本的實現更加簡潔、有序、精妙,可以說是實現了質的飛躍,緊跟世界潮流。這篇文章對其實現細節進行分析。 整個實現過程初看起來有點復雜,但只要掌握了其前后的遞推關系,理解起來就可以豁然開朗。 接著上一篇,我們分析了`runWithRequest`方法的前兩行代碼,接著分析它后面的代碼: ``` protected function runWithRequest(Request $request) { . . . return $this->app->middleware->pipeline() ->send($request) ->then(function ($request) { return $this->dispatchToRoute($request); }); } ``` 中間件的執行都在最后的`return`語句中。 # pipeline、through、send方法 `$this->app->middleware->pipeline()`的` pipeline`方法: ``` public function pipeline(string $type = 'global') { return (new Pipeline()) // array_map將所有中間件轉換成閉包,閉包的特點: // 1. 傳入參數:$request,請求實例; $next,一個閉包 // 2. 返回一個Response實例 ->through(array_map(function ($middleware) { return function ($request, $next) use ($middleware) { list($call, $param) = $middleware; if (is_array($call) && is_string($call[0])) { $call = [$this->app->make($call[0]), $call[1]]; } // 該語句執行中間件類實例的handle方法,傳入的參數是外部傳進來的$request和$next // 還有一個$param是中間件接收的參數 $response = call_user_func($call, $request, $next, $param); if (!$response instanceof Response) { throw new LogicException('The middleware must return Response instance'); } return $response; }; // 將中間件排序 }, $this->sortMiddleware($this->queue[$type] ?? []))) ->whenException([$this, 'handleException']); } ``` `through`方法代碼: ``` public function through($pipes) { $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this; } ``` 前面調用 `through` 傳入的 `array_map(...) `把所有中間件逐個封裝為閉包,然后,`through`則是把這些閉包保存在Pipeline類的 `$pipes` 屬性中。 PHP的 `array_map` 方法簽名: ``` array_map ( callable $callback , array $array1 [, array $... ] ) : array ``` $callback迭代作用于每一個 $array的元素,返回新的值。所以,最后得到`$pipes`中每個閉包的形式特征是這樣的(偽代碼): ``` function ($request, $next) { $response = handle($request, $next, $param); return $response; } ``` 該閉包接收兩個參數,一個是請求實例,一個是回調用函數,handle方法處理后得到相應并返回。 記住這個結構,對后面的分析非常重要。 `through` 返回一個Pipeline類的實例,接著調用`send`方法: ``` public function send($passable) { $this->passable = $passable; return $this; } ``` 該方法很簡單,只是將傳入的請求實例保存在`$passable`成員變量,最后同樣返回`Pipeline`類的實例,這樣就可以鏈式調用Pipeline類的其他方法。 # then,carry方法 `send`方法之后,接著調用`then`方法: ``` return $this->app->middleware->pipeline() ->send($request) ->then(function ($request) { return $this->dispatchToRoute($request); }); ``` 這里的`then`接收一個閉包作為參數,這個閉包實際上包含了控制器操作的執行代碼。 `then`方法代碼: ``` public function then(Closure $destination) { $pipeline = array_reduce( //用于迭代的數組(中間件閉包),這里將其倒序 array_reverse($this->pipes), // array_reduce需要的回調函數 $this->carry(), //這里是迭代的初始值 function ($passable) use ($destination) { try { return $destination($passable); } catch (Throwable | Exception $e) { return $this->handleException($passable, $e); } }); return $pipeline($this->passable); } ``` `carry`代碼: ``` protected function carry() { // 1. $stack 上次迭代得到的值,如果是第一次迭代,其值是后面的「初始值 // 2. $pipe 本次迭代的值 return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { try { return $pipe($passable, $stack); } catch (Throwable | Exception $e) { return $this->handleException($passable, $e); } }; }; } ``` 為了更方便分析原理,我們把`carry`方法內聯到`then`中去,并去掉錯誤捕獲的代碼,得到: ``` public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { return $pipe($passable, $stack); }; }, function ($passable) use ($destination) { return $destination($passable); }); return $pipeline($this->passable); } ``` 這里關鍵是理解`array_reduce`以及`$pipeline($this->passable)`的執行過程,這兩個過程可以類比于「包洋蔥」和「剝洋蔥」的過程。 `array_reduce`第一次迭代,`$stack`初始值為: **(A)** ``` function ($passable) use ($destination) { return $destination($passable); }); ``` 回調函數的返回值為: **(B)** ``` function ($passable) use ($stack, $pipe) { return $pipe($passable, $stack); }; ``` 將A代入B可以得到第一次迭代之后的`$stack`的值: **(C)** ``` function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($destination) { return $destination($passable); }) ); }; ``` 第二次迭代,同理,將C代入B可得: **(D)「洋蔥」** ``` // 偽代碼 // 每一層的$pipe都代表一個中間件閉包 function ($passable) use ($stack, $pipe) { return $pipe($passable, //倒數第二層中間件 function ($passable) use ($stack, $pipe) { return $pipe($passable, //倒數第一層中間件 function ($passable) use ($destination) { return $destination($passable); //包含控制器操作的閉包 }) ); }; ); }; ``` 以此類推,有多少個中間件,就代入多少次,最后一次得到`$stack`就返回給`$pipeline`。由于前面對中間件閉包進行了倒序,排在前面的閉包被包裹在更里層,所以倒序后的閉包越是后面的在外面,從正序來看,則變成越前面的中間件在最外層。 層層包裹好閉包后,我們得到了一個類似洋蔥結構的「超級」閉包**D**,該閉包的結構如上面的代碼注釋所示。最后把`$request`對象傳給這個閉包,執行它:`$pipeline($this->passable);`,由此開啟一個類似剝洋蔥的過程,接下來我們看看這洋蔥是怎么剝開的。 # 剝洋蔥過程分析 回顧上文,`array_map(...)`把每一個中間件類加工成一個類似這種結構的閉包: ``` function ($request, $next) { $response = handle($request, $next, $param); return $response; } ``` 其中`handle`是中間件中的入口,其結構特點是這樣的: ``` public function handle($request, $next, $param) { // do sth ------ M1-1 / M2-1 $response = $next($request); // do sth ------ M1-2 / M2-2 return $response; } ``` 我們上面的 D「洋蔥」一共只有兩層,也就是有兩層中間件的閉包,假設M1-1,M1-2分別是第一個中間件handle方法的前置和后置操作點位,第二個中間件同理,前置和后置點位分別是M2-1,M2-2。現在,讓程序執行`$pipeline($this->passable)`,展開來看,也就是執行: ``` // 偽代碼 function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($destination) { return $destination($passable); }) ); }; ); }($this->passable) ``` 此時,程序要求從: ``` return $pipe($passable, function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($destination) { return $destination($passable); }) ); }; ); ``` 返回值,也就是要執行第一個中間件閉包,`$passable`對應`handle`方法的`$request`參數,而下一層閉包 ``` function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($destination) { return $destination($passable); }) ); } ``` 則對應`handle`方法的`$next`參數。 示意圖: ![](https://img.kancloud.cn/66/cc/66cc0be2e30a98ef35ce6282b4cd2bfa_708x268.png) 要執行第一個閉包,即要執行第一個閉包的`handle`方法,其過程是:首先執行**M1-1**點位的代碼,即前置操作,然后執行`$response = $next($request);`,這時程序進入執行下一個閉包,`$next($request)`展開來,也就是: ``` function ($passable) use ($stack, $pipe) { return $pipe($passable, function ($passable) use ($destination) { return $destination($passable); }) ); }($request) ``` 依此類推,執行該閉包,即執行第二個中間件的`handle`方法,此時,先執行**M2-1**點位,然后執行`$response = $next($request)`,此時的`$next`閉包是: ``` function ($passable) use ($destination) { return $destination($passable); }) ``` 屬于洋蔥之芯——最里面的一層,也就是包含控制器操作的閉包,執行這個閉包: ``` function ($passable) use ($destination) { return $destination($passable); })($request) ``` 最終,我們從`return $destination($passable)`中返回一個`Response`類的實例(具體怎么返回的,后面再作分析),也就是,第二層的`$response = $next($request)`語句成功得到了結果,接著執行下面的語句,也就是**M2-2**點位,最后第二層閉包返回結果,也就是第一層閉包的`$response = $next($request)`語句成功得到了結果,然后執行這一層閉包該語句后面的語句,即**M1-2**點位,該點位之后,第一層閉包也成功返回結果,于是,then方法最終得到了返回結果。 整個過程過來,程序經過的點位順序是這樣的:**M1-1→M2-1→控制器操作→M2-2→M1-2→返回結果**。 # 總結 整個過程看起來雖然復雜,但不管中間件有多少層,只要理解了前后兩層中間件的這種遞推關系,洋蔥是怎么一層層剝開又一層層返回的,來多少層都不在話下。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看