laravelの起動プロセス中に何度も登場するパイプラインは、laravelの起動プロセスやライフサイクルを理解する上で重要なポイントの一つです。パイプラインについての説明はインターネット上にほとんどないので、自分で書いてみます。
まず、コールスタック、つまり、リクエストの開始からレスポンスが返されるまでのlaravelの動作を見てみましょう。
次のようにルーティングを設定します
Route::get('/', function() { return debug_backtrace();});
次に、laravel に付属する Web サーバーを起動します
php artisan serve
laravel に付属するサーバーを起動します
次に、localhost:8000/
にアクセスします。印刷されたコールスタックが表示されます。は非常に長いです... …
コールスタックを出力します
左下隅にあるように、リクエストの最初から最後まで、何も行われていないにもかかわらず、39 個のクラスがロードされたままです。 .. 優れたパフォーマンスを追求する学生は、慎重に検討する必要があります…
私たちが注目するのはパイプラインです。上記の手順に従うと、頻繁に呼び出されるパイプラインがあることがわかります。
それでは何に使われるのでしょうか?
簡単に言うと、Laravelでミドルウェアを実装することです。ミドルウェア コードについてよく考えてください
public function handle($request, Closure $next) { //do something for $request return $next($request);}
最終リクエストを取得する前に、リクエストはミドルウェア処理の層を通過します。これはどのように実装されますか?答えはパイプラインです
まずはパイプラインのマクロ体験と使い方、そして詳しく解説していきます
ソースコードでのパイプラインの使い方
ご覧のとおり、3つありますmain メソッドとパイプラインのすべてのメソッドが使用可能に公開されています。理解しやすいこと。次に、パイプラインがミドルウェアで使用されていると仮定し、送信オブジェクトは $request であると考えてください。実際、これは、ここで送信されるように設定されているものです。 . オブジェクトは後で渡され、A が処理された後、処理のために B に送信され、次に処理のために C に送信されます。 png
/** * Set the object being sent through the pipeline. * * @param mixed $passable * @return $this */public function send($passable) { $this->passable = $passable; return $this;}
(new Pipeline($this)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter());
その後
まずコードを見てみましょう
/** * Set the array of pipes. * * @param dynamic|array $pipes * @return $this */public function through($pipes){ $this->pipes = is_array($pipes) ? $pipes : func_get_args(); return $this;}
ほんの数行のように見えますが、理解するのは簡単ではありません (これが私がlaravelを好む理由です。コードはシンプルでエレガントですが、ほとんどの人はそれを理解できません) 、笑)
まずは、 について簡単に説明します。 次に、メソッドは、匿名関数 $destination を受け取る必要があります。 どのような匿名関数かについては、今はそのままにしておいて、後で確認することができます。一文ずつ説明しましょう rree
それでは?
もう、これですべての作業は完了です。非常にエレガントな (不正な) コードではないでしょうか? (笑) 次に、この array_reduce が返す魔法の関数に焦点を当てます。$request を渡すだけです。それにすべてのミドルウェアを渡しますか?
/** * Run the pipeline with a final destination callback. * * @param \Closure $destination * @return mixed */public function then(Closure $destination){ $firstSlice = $this->getInitialSlice($destination); $pipes = array_reverse($this->pipes); return call_user_func( array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable); );}
これを見たときはちょっとクラクラしてしまいました。なんてこった!
これを単純化しています。つまり、$this->getSlice() を呼び出した後、関数 B であると仮定して関数を取得します。関数 B は 2 つのパラメーターを受け取り、関数 C を返します
function haha($stack, $middleware) { return function($param) use ($stack, $middleware){ //do something };}function fake_array_reduce($middlewares, $func, $inital=null) { $temp = $initial; //假设传递进来的函数$func = 'haha' foreach($middlewares as $middleware) { $temp = $func($temp, $middleware); } return $temp;}
这个过程的基本流程如上,haha返回了一个空函数(实际上肯定不是),我只是为了说明每次都返回一个函数作为下一次迭代的参数,而并没有执行任何其它功能,按照上面的代码,最后执行完fake_array_reduce会得到一个函数,这个函数接收一个参数。
所以再回过头来看
call_user_func(array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable);
实际上就是相当于调用了array_reduce最后一次迭代返回的函数,然后给这个函数传递了一个参数$this->passable
所以,这时候我们需要关注最后一次返回的函数是什么,看看源码关键是这段
return function($stack, $pipe) { return function($passable) use ($stack, $pipe) { if($pipe instanceof Closure) { return call_user_func($pipe, $passable, $stack); } else { //.. } }}
每次迭代传入了当前的$pipe和上一次迭代返回的函数
IMG_0687.JPG
由上图所示
由于把pipes先反转了一下,所以最后一次迭代array_reduce得到的函数f3所use的是($stack=f2, $pipe=$middleware[0])
那么call_user_func(f3,$this->passable)相当于是
return call_user_func($middleware[0]->handle, $this->passable, f2);
仔细想想,middleware里面的handle方法
public function handle($request, Closure $next) { //... return $next($request);}
在这里就相当于就是return f2($request)
也就相当于return call_user_func($middleware[1]->handle, $request, f1)
而这个$request是经过middleware[0]处理过后传递过来的。
……
这个过程比较绕,需要大家自己再画图理解一下,比如源代码里用到的onion(洋葱),$stack(栈),这些单词可以帮助大家更好地理解这个过程。
所以return call_user_func($pipes, $this->getSlice(), $this->passable)就会把$request请求按middleware定义的先后顺序,流水线处理,中间件处理完了之后再传递给$this->dispatchToRouter()这个方法处理(这又是一个抽象),从而得到最后的响应结果。
理解pipeline需要理解 闭包 栈等概念,熟悉了pipeline会对理解php的新特性有比较大的帮助。
That's all thanks来自: http://www.jianshu.com/p/3c2791a525d0