laravel5.5框架解析[4]——Pipeline

laravel5.5框架解析系列文章属于对laravel5.5框架源码分析,如有需要,建议按顺序阅读该系列文章, 不定期更新,欢迎关注

pipeline 狗不理包子打狗, 有进有出

function testPipelineBasic()
{   
    // 创建管道
    $pipe = new Pipeline(new Container());
    // 发送 1
    $result = $pipe->send(1)
        // 设置管道节点
        ->through([
            // 将数据乘3
            function($v, $next){$v *= 3; return $next($v);},
            // 将数据加2
            function($v, $next){$v += 2; return $next($v);}
        ])
        // 返回格式化的结果
        ->then(function ($v) {
            return "value is $v";
        });
    // 5 <- (1 * 3 +2)
    $this->assertEquals('value is 5', $result);
}

怎么实现的呢, 直接看源码Illuminate\Pipeline\Pipeline

class Pipeline implements PipelineContract
{
    // 容器
    protected $container;

    // 被发送的值
    protected $passable;

    // 管道节点数组
    protected $pipes = [];

    // 管道节点是个类的时候,该调用的方法名
    protected $method = 'handle';
    // 构造函数
    public function __construct(Container $container = null)
    {
        $this->container = $container;
    }

    // 设置被发送内容, 返回$this 以便链式调用
    public function send($passable)
    {
        $this->passable = $passable;

        return $this;
    }

    // 设置节点
    public function through($pipes)
    {
        $this->pipes = is_array($pipes) ? $pipes : func_get_args();

        return $this;
    }

    // 设置节点类被调用方法名
    public function via($method)
    {
        $this->method = $method;

        return $this;
    }

    // 经管道节点发送值到达$destination回调, 然后经管道节点返回结果
    public function then(Closure $destination)
    {
        // 将一系列节点变成一个闭包, 不知道array_reduce的,请先参考php.net
        $pipeline = array_reduce(
            // 节点数组翻转, 这是因为reduce之后,在数组前面的会被最后调用, 一会就能看到
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );
        
        // 通过这个调用这个闭包就完成了管道发送过程
        return $pipeline($this->passable);
    }
    
    // 包装一下上面的destination回调, 使得成为管道中最后一个节点
    protected function prepareDestination(Closure $destination)
    {
        return function ($passable) use ($destination) {
            return $destination($passable);
        };
    }

    // 这里是重点
    protected function carry()
    {   
        // 这里返回一个闭包, 供上面array_reduce使用, 
        // $stack, 上一个节点传来的, 就是这一个节点所需的$next闭包
        // pipe,当前节点
        return function ($stack, $pipe) {
            // 包装一个节点, 包装成下一个节点的$next,
            // 以便这些节点能通过next方法串起来,注意这个闭包就是下一个节点的$next
            return function ($passable) use ($stack, $pipe) {
                // 这里面的流程是在下一个节点调用$next的时候执行的, 这里会把值传给当前节点处理,
                // 由于这里的流程需要后面的节点用$next触发, 所以在节点数组前面的会排在后面被调用
                if (is_callable($pipe)) {
                    // 如果节点是个闭包,直接调用闭包
                    return $pipe($passable, $stack);
                } elseif (! is_object($pipe)) {
                    // 如果是字符串, 会解析出类名和参数
                    list($name, $parameters) = $this->parsePipeString($pipe);
                    
                    //通过容器, 把类名或者是alias取出实例
                    $pipe = $this->getContainer()->make($name);
                    // 参数合并, 前2个是必须的, 后面的是附加参数
                    $parameters = array_merge([$passable, $stack], $parameters);
                } else {
                    // 如果节点是个实例,那直接传参
                    $parameters = [$passable, $stack];
                }
                
                // 这里调用节点得到该节点返回的值
                return method_exists($pipe, $this->method)
                                ? $pipe->{$this->method}(...$parameters)
                                : $pipe(...$parameters);
            };
        };
    }

    // 解析类名,参数
    protected function parsePipeString($pipe)
    {
        list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);

        if (is_string($parameters)) {
            $parameters = explode(',', $parameters);
        }

        return [$name, $parameters];
    }

    // 获取Container
    protected function getContainer()
    {
        if (! $this->container) {
            throw new RuntimeException('A container instance has not been passed to the Pipeline.');
        }

        return $this->container;
    }
}

主要就在于理解carry方法得到的闭包. 有不懂的, 可以在下边留言

你可能感兴趣的:(laravel5.5框架解析[4]——Pipeline)