PHP-因为Laravel路由中间件引发的疑惑

Posted by

中间件,听着很久了,但是它到底是怎么实现的?刚开始看到Lumen这其中的处理方式时,几行代码,硬是没看懂,一度怀疑自己。。。回味了好久,总算是可以体会到了,它的基础实现方式是如何运作的。

下面结合简单代码,通过一个简化的流程,体会一下核心应用,看看会输出什么

$array = [
    'tom',
    'jane',
    'dark'
];

function show($origin, $item)
{
    $callable =  static function() use ($origin, $item) {
        if (is_string($origin)) {
            var_dump($origin);
        }
        var_dump($item);
        if (is_callable($origin)) {
            $origin();
        }
    };
    return $callable;
}

$go = array_reduce($array, 'show', 'prepare');
$go();

——以上代码将输出

首先,我们需要理解一下函数array_reduce。个人理解:将数组中的每个元素使用回调元素,简化成一个单一的值。回调函数第一个参数为当前上一次回调结束后返回的值,如果是第一次,那么这个值为array_reduce指定的第三个参数,initial;第二个值为此次数组传递的元素。
官方文档解释为:array_reduce(array, callback[, initial = null] ) 将回调函数 callback 迭代地作用到 array 数组中的每一个单元中,从而将数组简化为单一的值。

在上述的示例中,回调函数show,返回的是一个匿名函数,在分析的过程中,我们先不去理会返回的匿名函数具体是如何执行的,我们只需要认识到,这是一个匿名函数,只有在被触发之后,才会运行。现在按照执行顺序整体解析一遍。

1、array_reduce,调起show,传入第一个参数:字符串prepare,和第二个参数:数组第一个元素tom,show的内部执行,是返回一个闭包,我们称之为A-tom,参数是origin:prepare;item:tom
2、array_reduce,调起show,传入第一个参数:闭包A-tom,和第二个参数:数组第二个元素jane,show的内部执行,依旧返回一个闭包,我们称之为B-jane,参数是origin:A-tom;item:jane
3、array_reduce,调起show,传入第一个参数:闭包B-jane,和第二个参数:数组第三个元素dark,show的内部执行,依旧返回一个闭包,我们称之为C-dark,参数是origin:B-jane;item:dark

4、最后go得到一个闭包,即C-dark

5、go被执行

6、闭包(B-jane,dark)执行,B-jane不是字符串,所以不进入,然后打印dark,此时屏幕输出【dark】,然后B-jane是可回调,则执行B-jane
7、B-jane同样是一个闭包,它的成员是origin:A-tom;item:jane,因此首先A-tom不是字符串,所以不进入,然后打印jane,此时屏幕输出【jane】,然后A-tom可回调,则执行A-tom
8、A-tom同样是一个闭包,它的成员是origin:prepare;item:tom,首先prepare是字符串,所以进入,打印prepare,此时输出【prepare】,然后打印tom,此时屏幕输出【tom】,最后prepare不是可回调,因此不进入。结束

所以整个过程下来,屏幕输出
dark
jane
prepare
tom

熟悉整个过程之后,我们更全局的看一遍整个过程:在array_reduce对每个元素执行show方法的时候,我们可以看做为一次又一次的不断对一个对象包装,而最初的第一个元素就在整个最终对象的最里面,最后执行的在最外层。这个可以比作为入栈的过程,而最后这个对象被执行的时候,就是一层一层的从最外面去拆开,去执行它,仿佛就是在剥洋葱,也可以比作是在出栈。
所以三个元素,最后进入的,最先被执行打印出来,然后第二层,第三层。在第三层的时候,它是最底层了,闭包的第一个参数已经是一个字符串了,不是闭包了,所以遇到了判断为字符串时,此时为真,所以先输出了prepare,然后接着遇到了打印item,输出tom。

而在具体的框架实现中,中间件show方法为类似这样的功能:

function carry($closures, $middleware)
{
    return function() use($closures, $middleware) {
        return $middleware->handle($closures);
    };
}

每个中间件在被执行的时候,可以自己处理自己的事情:

class ExampleMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        echo 'before';
        return $next($request);
        echo 'after';
    }
}

当然,实际的框架比这个复杂的多,以上只是针对实现的底层原理基础进行了一个剖析,后面还是得尝试去看看框架中的代码

Leave a Reply

邮箱地址不会被公开。 必填项已用*标注