この記事の例では、Laravel のミドルウェア実装原則について説明します。参考のために皆さんと共有してください。詳細は次のとおりです:
#1 ミドルウェアとは何ですか?
Web アプリケーションの場合、リクエストが実際に処理される前に、リクエストがより深いレベルに渡される前に、リクエストに対してさまざまな判断が行われることがあります。このように if else を使用すると、判断する条件が増えるとコードの保守が難しくなり、システム間の結合が増加しますが、この問題はミドルウェアで解決できます。これらの判断をミドルウェアに分離し、リクエストを簡単にフィルタリングできます。
#2 Laravel のミドルウェア
Laravel では、ミドルウェアの実装は実際には IlluminatePipelinePipeline クラスの実装に依存しています。まず、ミドルウェアをトリガーするコードを見てみましょう。これは非常に簡単で、処理後にリクエストをクロージャに転送し、引き続きそのリクエストを渡し続けるだけです。
public function handle($request, Closure $next) { //do something for $request return $next($request); }
#3 ミドルウェアの内部実装
前述の通り、ミドルウェアはPipelineで実装されており、その呼び出しはIlluminateRoutingRouter内にあります
return (new Pipeline($this->container)) ->send($request) ->through($middleware) ->then(function ($request) use ($route) { return $this->prepareResponse( $request, $route->run($request) ); });
ミドルウェアの実行処理が3つのメソッドで呼ばれていることが分かります。これら 3 つのメソッドのコードを見てみましょう:
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 の 2 番目のパラメーターには関数が必要なので、ここでは 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 を返します。なぜ 2 つの関数を返すのでしょうか? $next($request) を使用して転送プロセス中にオブジェクトを渡します。 $next($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 が最外層にあるためです。 array_reduce はミドルウェア コードを実行するのではなく、ミドルウェアをラップすることに注意してください。
これを見れば理解できるはずですが、array_reduce は最終的に func3 を返し、call_user_func(func3,$this->passable) は実際には
return call_user_func($middleware[0]->handle, $this->passable, func2);
であり、ミドルウェアのハンドル コードは次のとおりです:
public function handle($request, Closure $next) { return $next($request); }
これは return func2 と同等です。 ($request)、ここでの $request は前のミドルウェアによって処理されます。これで Zhengguo ミドルウェアのプロセスは終了しましたが、少しわかりにくいかもしれませんが、最終的には一番外側の call_user_func がミドルウェア コードを実行するということを覚えておいてください
この記事が、皆様の PHP プログラム設計に役立つことを願っています。 Laravel フレームワーク。
Laravel ミドルウェアの実装原理の詳細な説明と関連記事については、PHP 中国語 Web サイトに注目してください。