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