page contents

Laravel 中间件解析

所有的后置操作,都是执行到最内层,递归往回走时才会执行逐层执行。

attachments-2020-06-y6Or5ycE5ee04c051ae78.png

Laravel 中间件

代码展示

protected function sendRequestThroughRouter($request)
{
    # $this->app->instance('request', $request);

    # Facade::clearResolvedInstance('request');

    # $this->bootstrap();

    return (new Pipeline($this->app))
                ->send($request)
                ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                ->then($this->dispatchToRouter());      // $this->dispatchToRouter(),后期继续
}

new \Illuminate\Routing\Pipeline($this->app):
public function __construct(Container $container = null)
{
    $this->container = $container;
}
public function send($passable)
{
    $this->passable = $passable;

    return $this;
}
public function through($pipes)
{
    ####################################################################################
    #    $this->middleware = [                                                         #
    #        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,    #
    #        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,           #
    #        \App\Http\Middleware\TrimStrings::class,                                  #
    #        \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,  #
    #    ];                                                                            #
    ####################################################################################
    $this->pipes = is_array($pipes) ? $pipes : func_get_args();

    return $this;
}
// 中间件的本质
public function then(Closure $destination)
{
    $pipeline = array_reduce(
        array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
    );
    // 上面操作完之后返回一个匿名函数,接受一个参数
    return $pipeline($this->passable);
}
// 注意这个是子类里面会调用的父carry,区别在于子类加入了异常处理
protected function carry()
{
    // 接受匿名函数$stack参数和$pipe参数,返回匿名函数,再将此匿名函数作为第一个参数$stack迭代传入。
    return function ($stack, $pipe) {
        return function ($passable) use ($stack, $pipe) {
            if ($pipe instanceof Closure) {
                return $pipe($passable, $stack);
            } elseif (! is_object($pipe)) {
                list($name, $parameters) = $this->parsePipeString($pipe);

                $pipe = $this->getContainer()->make($name);

                $parameters = array_merge([$passable, $stack], $parameters);
            } else {
                $parameters = [$passable, $stack];
            }
            // 中间件作用是提供了一种方便的机制来过滤进入应用的 HTTP 请求
            // $this->method默认为handle,可通过via方法进行设置
            return $pipe->{$this->method}(...$parameters);
        };
    };
}
protected function parsePipeString($pipe)
{
    // 不带参数的pipe(class)或带参数(参数以,分割)的pipe(class:param1,param2,param3...)
    list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);

    if (is_string($parameters)) {
        $parameters = explode(',', $parameters);
    }
    // $parameters为[]数组或[param1,param2,param3...]
    return [$name, $parameters];
}
protected function prepareDestination(Closure $destination)
{
    return function ($passable) use ($destination) {
        return $destination($passable);
    };
}

前置条件

  1. array_reduce接受三个参数,第一个参数接收数组,第二个参数函数名(也可以是匿名函数,函数有两个参数,分别代表$result和$item),第三个参数(可选),该参数将被当成是数组中的第一个值来处理,或者如果数组为空的话就作为最终返回值。
  2. 匿名函数也叫闭包函数(closures),允许临时创建一个没有指定名称的函数,通过 Closure 类来实现的。
  3. 当对闭包函数进行赋值时,PHP 便会自动将此种表达式转换成内置类 Closure 的对象对象在进行赋值。
  4. 当闭包函数使用到 use 使用外部数据时,会在 Closure 对象生成一个 static 属性数组进行存放。
  5. 当闭包函数使用到参数时,会在 Closure 对象生成一个 parameter 属性数组进行存放。
public function then(Closure $destination)
    {
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );
        return $pipeline($this->passable);
    }

生成最终匿名函数的过程:

array_reduce执行第一次时得到如下简化的匿名函数返回,将会继续作为第一个参数进行迭代:        
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#1 (0) { // $this->prepareDestination($destination)
            }
            ["pipe"]=>
            string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
          }
        }
    第二次:
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#id (1) {
              ["static"]=>
              array(2) {
                ["stack"]=>
                object(Closure)#1 (0) { // $this->prepareDestination($destination)
                }
                ["pipe"]=>
                string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
              }
            }
            ["pipe"]=>
            string(15) "App\Http\Middleware\TrimStrings"
          }
        }
    第三次:
        object(Closure)#id (1) {
          ["static"]=>
          array(2) {
            ["stack"]=>
            object(Closure)#id (1) {
              ["static"]=>
              array(2) {
                ["stack"]=>
                object(Closure)#id (1) {
                  ["static"]=>
                  array(2) {
                    ["stack"]=>
                    object(Closure)#1 (0) { // $this->prepareDestination($destination)
                    }
                    ["pipe"]=>
                    string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
                  }
                }
                ["pipe"]=>
                string(15) "App\Http\Middleware\TrimStrings"
              }
            }
            ["pipe"]=>
            string(15) "Illuminate\Foundation\Http\Middleware\ValidatePostSize"
          }
        }

依次类推,最终得到一个匿名函数如下(接受一个参数,此匿名函数内部使用上面递归形式的$pipe和$stack)。

function ($passable) {
        if ($pipe instanceof Closure) {
            return $pipe($passable, $stack);
        } elseif (! is_object($pipe)) {
            list($name, $parameters) = $this->parsePipeString($pipe);

            $pipe = $this->getContainer()->make($name); // 实例化middleware

            $parameters = array_merge([$passable, $stack], $parameters);
        } else {
            $parameters = [$passable, $stack];
        }
        return $pipe->{$this->method}(...$parameters);  // 语法糖模式,因为middleware参数可有可无
    };

最终匿名函数的调用过程(从最外层开始,这就是前面为什么要array_reverse,一层一层往里拨,整体上的处理是:$pipe若是匿名函数,直接调用。若是字符串,则解析成对应的类和参数,make类,再组成参数数组。最后调用$pipe->handle)

最外层($pipe=Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode):
    (new \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode)->handle($passable, $stack)
    执行完前置操作后,调用$stack($passable),继续进行下一层

    下一层($pipe=Illuminate\Foundation\Http\Middleware\ValidatePostSize):
    (new \Illuminate\Foundation\Http\Middleware\ValidatePostSize)->handle($passable, $stack)
    执行完前置操作后,调用$stack($passable),继续进行下一层

    **以此类推,当每层中间件的前置任务全部完成,即递归执行到最里面一层(路由分发,解析请求,返回响
    应),再由最内层一层一层往回走,执行每层中间件的后置任务。至此,返回本次请求的响应。**

最内层(即路由分发,解析请求,返回响应)的操作代码展示,后续分析:即执行$destination($passable)匿名函数,位于下面方法中。

protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            return $destination($passable);
        };
    }
    $destination:
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);

            return $this->router->dispatch($request);
        };
    }

说明:
所有的后置操作,都是执行到最内层,递归往回走时才会执行逐层执行。


attachments-2020-06-zH9qKM9o5ee04be992210.jpg

  • 发表于 2020-06-10 10:57
  • 阅读 ( 632 )
  • 分类:PHP开发

0 条评论

请先 登录 后评论
Pack
Pack

1135 篇文章

作家榜 »

  1. 轩辕小不懂 2403 文章
  2. 小柒 1316 文章
  3. Pack 1135 文章
  4. Nen 576 文章
  5. 王昭君 209 文章
  6. 文双 71 文章
  7. 小威 64 文章
  8. Cara 36 文章