Understanding pipeline in Laravel

Source: Internet
Author: User
Tags what magic
Understanding pipeline in Laravel appears many times during laravel startup. to understand the startup process and lifecycle of laravel, understanding pipeline is one of the key points. Pipeline is rarely explained on the Internet, so I should write it myself.
First, let's take a look at the call stack, that is, what laravel has done from a request to a response.
Configure the following in routing:

Route::get('/', function() {    return debug_backtrace();});

Then start the web server provided by laravel.

php artisan serve


Start the server provided by laravel


Then access localhost: 8000/
We can see that the printed call stack is very long ......


Print call stack


From the lower left corner, we can see that, from the request to the end, 39 classes are still loaded even though nothing is done ...... Those who are pursuing superior performance need to think about it ......
Our focus is on pipeline. if you follow the steps above, you will find that there is a pipeline called a lot of times.
So what is it?
To put it simply, it is used to implement middleware in Laravel. Think about the middleware code

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

A request is processed by a layer-by-layer middleware to obtain the final request. is this implemented? The answer is pipeline.
First, let's take a macro look at how to use pipeline. then let's talk about it.


Use of pipeline in source code


As you can see, there are three main methods, which are all the methods that pipeline exposes to the user.

  • Send
  • Through
  • Then
Send method
/**  * Set the object being sent through the pipeline. *  * @param  mixed  $passable  * @return $this */public function send($passable) {        $this->passable = $passable;        return $this;}

From the description, it is the object passed in the pipeline. it may not be easy to understand. Then we assume that pipeline is used in middleware, and the send object is $ request.
Think about the middleware code above. The first parameter received by each handle method is $ request, which is actually the object set here to be sent. it will be passed everywhere afterwards, after A finishes processing, it is sent to B for processing and then to C for processing ...... Pipeline, image

Through


Screen snapshot 9.05.36.png


As the name implies: through, via, is the middleware used to process the objects set by the send above.
The above macro description shows the usage of pipeline when calling through.

(new Pipeline($this))    ->send($request)    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)    ->then($this->dispatchToRouter());

See the parameters passed in through. if the middleware is not required for the current request, an empty array will be passed. Otherwise, the predefined middleware array will be passed.
The code in pipeline is as follows:

/**   * 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;}

This looks very simple. you can either pass an array or multiple strings, and it will be combined into an array. it doesn't matter. it's just like specifying the middleware name anyway.

Then

The next step is the core and the obscure part of the code.
The then method is to issue a command to start processing when everything is ready.
We can also see that both send and through are set, and nothing else is done. so when the objects to be passed and the middleware to be passed are set, then? Of course, it's just getting started!
Let's take a look at then's code first.

/** * 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);    );}

It looks like a few lines, but it is not easy to understand (this is why I like laravel very much. the code is simple and elegant, and most people cannot appreciate it, haha)
Let me talk about it first.
The then method needs to receive an anonymous function $ destination. you don't need to worry about what kind of anonymous function, so let's look at it later. One sentence

Public function then (Closure $ destination) {// wrap the passed anonymous function into a Closure. it doesn't matter if you don't quite understand it, knowing that $ firstSlice is a function is just fine. $ firstSlice = $ this-> getInitialSlice ($ destination); // the parameters passed in by the through method, that is, the middleware array is reversed, why? // Simply put, the stack features are used. you can continue to read $ pipes = array_reverse ($ this-> pipes); // array_reduce (...) return A function A, execute this function, and pass the sent object as the parameter to function A return call_user_func (array_reduce ($ pipes, $ this-> getSlice (), $ firstSlice ), $ this-> passable ););}

What then?
No, this completes the entire job. isn't it very elegant? haha
Then we put our energy on this array_reduce. what magic function does it return? I just need to pass $ request to it, and all the middleware will be finished?
Array_reduce ($ arr, 'funca', 5) is roughly equivalent. The second parameter can be a string of the function name.

function fake_array_reduce($arr, $func, $initial=null)    $temp = $initial;    foreach($arr as $v) {        $temp = $func($temp, $v);    }    return $temp;}

Understand the meaning.
Return to the then source code. The second parameter passed in the code is a function, and it is a function obtained by executing $ this-> getSlice (). let's take a look at this getSlice method.

/**   * Get a Closure that represents a slice of the application onion.   *   * @return \Closure   */protected function getSlice(){        return function ($stack, $pipe) {                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)                );                        }                };        };}

I was a little dizzy when I saw it. what the hell is this closure!
To simplify the process, call $ this-> getSlice () and get a function. assume function B receives two parameters and returns a function C.
Let's roughly simulate this process.
Write a piece of false code to better illustrate the process:

Function haha ($ stack, $ middleware) {return function ($ param) use ($ stack, $ middleware) {// do something};} function fake_array_reduce ($ middlewares, $ func, $ inner = null) {$ temp = $ initial; // assume the passed function $ func = 'hahaha' foreach ($ middlewares as $ middleware) {$ temp = $ func ($ temp, $ middleware);} return $ temp ;}

The basic process of this process is as follows: haha returns an empty function (not actually). I just want to explain that a function is returned every time as a parameter for the next iteration, without executing any other functions, follow the code above and finally run fake_array_reduce to get a function that receives a parameter.
So let's look back.
Call_user_func (array_reduce ($ pipes, $ this-> getSlice (), $ firstSlice), $ this-> passable );
Actually, it is equivalent to calling the function returned by the last iteration of array_reduce, and then passing a parameter $ this-> passable to this function.
So, at this time, we need to pay attention to what is the last returned function. let's take a look at the key section of the source code.

 return function($stack, $pipe) {    return function($passable) use ($stack, $pipe) {        if($pipe instanceof Closure) {            return call_user_func($pipe, $passable, $stack);        } else {            //..        }    }}

Each iteration passes in the current $ pipe and the function returned by the previous iteration.


IMG_0687.JPG


By
Because pipes is reversed first, the f3 function obtained in the last iteration of array_reduce is used ($ stack = f2, $ pipe = $ middleware [0]).
Then call_user_func (f3, $ this-> passable) is equivalent

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

Think about the handle method in middleware.

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

Here it is equivalent to return f2 ($ request)
Return call_user_func ($ middleware [1]-> handle, $ request, f1)
This $ request is passed after being processed by middleware [0.
......
This process is quite difficult. you need to draw a picture on your own. for example, The onion (onion) and $ stack used in the source code can help you better understand this process.
Therefore, return call_user_func ($ pipes, $ this-> getSlice (), $ this-> passable) processes the $ request in the order defined by middleware and the pipeline, after the middleware completes processing, it passes the $ this-> dispatchToRouter () method for processing (this is an abstraction) to get the final response result.

To understand pipeline, you need to understand the concepts such as closure stack. if you are familiar with pipeline, it will be of great help to understand the new features of php.

That's all thanks

From: http://www.jianshu.com/p/3c2791a525d0

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.