Home  >  Article  >  PHP Framework  >  Analysis of ideas for creating laravel middleware

Analysis of ideas for creating laravel middleware

藏色散人
藏色散人forward
2020-03-29 09:07:371879browse

There are many implementation principles for parsing laravel middleware on the Internet, but I wonder if there are readers who don’t understand when reading. How did the author think of using the array_reduce function?

Recommended: laravel tutorial

This article starts from my own perspective and simulates how I would implement this middleware function if I were the author, and how to find and Use the corresponding function.

What is laravel middleware

Laravel middleware provides a mechanism to interrupt the original program flow and process it through middleware without modifying the logic code. Some events, or extend some functions. For example, logging middleware can easily record request and response logs without changing the logic code.

Then let’s simplify the software execution process. Now there is a core class kernel, and the following is its laravel code

#捕获请求
$request = Illuminate\Http\Request::capture()
#处理请求
$response = $kernel->handle($request);

The function of the code is to capture a Request and return a Response. Here is the code segment that is subsequently distributed to the specific execution logic and returns the results.

So if you want to add a piece of logic before or after executing the $kernel->handle() method, how would you write it? It's roughly as follows:

$request = Illuminate\Http\Request::capture()
function midware(){
    before()#在之前执行的语句集合
    #####    
    $response = $kernel->handle($request);
    #####
    after()#在之后执行的语句集合
}

Obviously there is no problem with writing this way, but it has no scalability. You have to change this method to execute anything. It is impossible to encapsulate it into the core content of the framework. How to improve it

Define a middleware class to be executed called middleware. The class implements two methods, before() and after(), and the code is as follows.

#配置项中有一项配置中间件:
middleware = '';
$request = Illuminate\Http\Request::capture()
function midware(){
    middleware.before()
    #####    
    $response = $kernel->handle($request);
    #####
    middleware.after()
}

Does it solve the problem? It solves the problem of not having to change it. But what if we need multiple middlewares? The easiest thing to think of is: define a middleware array middleware_arr, each middleware class Both contain before and after methods. The code is as follows:

#配置项中有middleware_arr
middleware_arr=array();
$request = Illuminate\Http\Request::capture()
function midware(){
    foreach(middleware_arr as middleware){
       middleware.before()
    }
    #####    
    $response = $kernel->handle($request);
    #####
    foreach(middleware_arr as middleware){
        middleware.after()
    }
}

Although it is a bit old-fashioned, it does solve the problem. But there is still a problem with this, that is, how do we pass parameters to the middleware? Is it ok as follows:

$request = Illuminate\Http\Request::capture()
function midware(){
    foreach(middleware_arr as middleware){
       middleware.before($request)
    }
    #####    
    $response = $kernel->handle($request);
    #####
    foreach(middleware_arr as middleware){
        middleware.after($response)
    }
}

It seems to solve the problem, but if you analyze it carefully, you will find that every time it is given The middleware is all the original $request, which obviously doesn't work. Change it to the following:

$request = Illuminate\Http\Request::capture()
function midware(){
    foreach(middleware_arr as middleware){
       $request = middleware.before($request)
    }
    #####    
    $response = $kernel->handle($request);
    #####
    foreach(middleware_arr as middleware){
        $response = middleware.after($response)
    }
}

Another question is, assuming there are two middlewares A and B, what should be the execution order:

$request = Illuminate\Http\Request::capture()
$request = A.before($request);
$request = B.before($request);
$response = $kernel->handle($request);
$response = A.after();
$response = B.after();

Is this reasonable? It’s not easy to tell. Let’s assume that there is a middleware that records request and response logs. At this time, no matter where you put it, it cannot perfectly record the initial request and the final request. log. Is it necessary to write two classes in a similar situation, one to record the request and place it first in the middleware array, and one to process the response and place it at the last place in the array? It is better to reverse the middleware_arr array before executing the subsequent foreach, so that it meets the requirements:

$request = Illuminate\Http\Request::capture()
$request = A.before($request);
$request = B.before($request);
$response = $kernel->handle($request);
$response = B.after();
$response = A.after();

But I also began to wonder whether there is a better solution to this old-fashioned and inflexible solution. When observing this execution sequence, we found that it is a wrapper style (onion style). Can we find a more flexible and elegant solution to the next problem? Looking at the above structure, it always feels a bit familiar. It is very much like the function of A wraps the function of B, and the function of B includes the initial execution code. It is easy to call functions within a function, but each middleware here does not know the existence of the other, so the functions to be executed by other middleware must be passed to the upper level. Here, a closure function and a php function are used. array_reduce(),

array_reduce function definition: mixed array_reduce ( array $input , callable $function [, mixed $initial = NULL ] )

<?php
 function  rsum ( $v ,  $w )
{
     $v  +=  $w ;
    return  $v ;
}
function  rmul ( $v ,  $w )
{
     $v  *=  $w ;
    return  $v ;
}
 $a  = array( 1 ,  2 ,  3 ,  4 ,  5 );
 $x  = array();
 $b  =  array_reduce ( $a ,  "rsum" );
 $c  =  array_reduce ( $a ,  "rmul" ,  10 );
 ?>   
 #输出:
这将使 $b  的值为 15, $c  的值为 1200(= 10*1*2*3*4*5)

array_reduce() will act on the callback function iteratively to each cell in the input array, thereby reducing the array to a single value. We wrap multiple functions into one function that is finally called.

#我们先假设只有一个middleware,叫log来简化情况,这里的类应该是一个类全路径,我这里就简单的写一下,要不然太长了。
    $middleware_arr = [&#39;log&#39;];
#最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。
    $default = function() use($request){
        return $kernel->handle($request);
    }
    $callback = array_reduce($middleware_arr,function($stack,$pipe) {
        return function() use($stack,$pipe){
          return $pipe::handle($stack);
        };
    },$default);
    
    
# 这里 callback最终是 这样一个函数:
    function() use($default,$log){
          return $log::handle($default);
        };
        
#所以每一个中间件都需要有一个方法handle方法,方法中要对传输的函数进行运行,类似如下,这里我类名就不大写了
    class log implements Milldeware {
        public static function handle(Closure $func)
        {
            $func();
        }
    }
    
#这里不难看出可以加入中间件自身逻辑如下:
 class log implements Milldeware {
        public static function handle(Closure $func)
        {
            #这里可以运行逻辑块before()
            $func();
            #这里可以运行逻辑块after()
        }
    }

In this way, when executing the callback function, the execution sequence is as follows:

Run the log::haddle() method first,

execute the log::before() method

Run the default method and execute $kernel->handle($request)

Run the log::after() method

Then simulate multiple situations as follows:

    $middleware_arr = [&#39;csrf&#39;,&#39;log&#39;];
#最终要执行的代码先封装成一个闭包,要不然没有办法传递到内层,如果用函数名传递函数的话,是没有办法传递参数的。
    $default = function() use($request){
        return $kernel->handle($request);
    }
    $callback = array_reduce($middleware_arr,function($stack,$pipe) {
        return function() use($stack,$pipe){
          return $pipe::handle($stack);
        };
    },$default);
    
    
# 这里 callback最终是 执行这样:
    $log::handle(function() use($default,$csrf){
                    return $csrf::handle($default);
                });

The execution sequence is as follows:

1. First run the log::haddle (including csrf::handle closure function) method,

2. Execute log::before () method

3. Running the closure means running $csrf::handle($default)

4. Executing the csrf::before() method

5. Run the default method and execute $kernel->handle($request)

6. Execute the csrf::after() method

7. Run the log::after() method

Note that another problem here is that the results generated by the middleware are not transferred. The same purpose can be achieved by modifying the shared resources. It is not necessary to actually pass the value to the next middleware.

This is the end of this document. In fact, many of the joints were only figured out when I wrote this article. In particular, I have a deeper understanding of the use and understanding of closure functions. Closure functions can delay the use of resources. For example, statements that are not suitable for execution at the moment need to be passed to later. Closures can be used to encapsulate and pass them out. This is something that traditional functions cannot do. Arrived.

The above is the detailed content of Analysis of ideas for creating laravel middleware. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete