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方法得到的闭包. 有不懂的, 可以在下边留言

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容