js reduce实现中间件_Laravel 中间件解析

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 IlluminateRoutingPipeline($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 = [                                                         #
    #        IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode::class,    #
    #        IlluminateFoundationHttpMiddlewareValidatePostSize::class,           #
    #        AppHttpMiddlewareTrimStrings::class,                                  #
    #        IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull::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) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull"
          }
        }
    第二次:
        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) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull"
              }
            }
            ["pipe"]=>
            string(15) "AppHttpMiddlewareTrimStrings"
          }
        }
    第三次:
        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) "IlluminateFoundationHttpMiddlewareConvertEmptyStringsToNull"
                  }
                }
                ["pipe"]=>
                string(15) "AppHttpMiddlewareTrimStrings"
              }
            }
            ["pipe"]=>
            string(15) "IlluminateFoundationHttpMiddlewareValidatePostSize"
          }
        }

依次类推,最终得到一个匿名函数如下(接受一个参数,此匿名函数内部使用上面递归形式的$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=IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode):
    (new IlluminateFoundationHttpMiddlewareCheckForMaintenanceMode)->handle($passable, $stack)
    执行完前置操作后,调用$stack($passable),继续进行下一层

    下一层($pipe=IlluminateFoundationHttpMiddlewareValidatePostSize):
    (new IlluminateFoundationHttpMiddlewareValidatePostSize)->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);
        };
    }

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

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家 ,需要请戳这里链接 或 者关注咱们下面的知乎专栏
PHP架构师圈子​zhuanlan.zhihu.com
f983dc6f81b6ef4b380b2b600ccd1928.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值