1. 背景
在Laravel中经常需要对一个对象,经过多个中间层处理后,才到真正处理的函数,Laravel将这种常用操作抽象出来,叫做Pipeline
2. 基本操作
interface Pipeline
{
/**
* Set the traveler object being sent on the pipeline.
*
* @param mixed $traveler
* @return $this
*/
public function send($traveler);
/**
* Set the stops of the pipeline.
*
* @param dynamic|array $stops
* @return $this
*/
public function through($stops);
/**
* Set the method to call on the stops.
*
* @param string $method
* @return $this
*/
public function via($method);
/**
* Run the pipeline with a final destination callback.
*
* @param \Closure $destination
* @return mixed
*/
public function then(Closure $destination);
}
基本操作就4个send,through,via,then
,一个常见的应用是
$pipeline->send($request)->through($middles)->then($handle);
非常直观的语义,在看实现之前,我们先自己来实现下这个功能
3. 动手实现
在实现之前,我们先看下下面一段代码
function foldLeft($f, $zero, array $list) {
if(empty($list)){
return $zero;
}
else {
$head = array_first($list);
$tails = array_slice($list,1);
return foldLeft($f,$f($zero,$head),$tails);
}
}
class PipelineTest extends \PHPUnit_Framework_TestCase{
public function testFoldLeft()
{
$arr = range(1,10);
$sum = foldLeft(function($a,$b){
return $a + $b;
},0,$arr);
$this->assertEquals(55,$sum);
}
}
上面功能很简单,通过foldLeft
函数,实现了数组求和,函数foldLeft
有3个参数,第一个是一个二元函数,第二个是一个初值,第三个则是需要操作的集合,组合起来可以通过一张图来说明:
让我们对foldLeft
函数更进一步,看下他的函数类型
foldLeft :: (b -> a -> b) -> b -> t a -> b
此处(b -> a -> b)
是函数$f
的类型,根据上面的定义,如果我们现在数组t a
中元素a
都是函数,而类型b
也是函数,这样子的话,此处a
相当于于处理对象的middle ware
,而b是具体的处理对象的函数,这样子讲可能比较抽象,让我们看下下面的例子:
public function testFoldLeftFunc()
{
$arr = [
function($a, $stack){
return $stack($a . " 1 ");
},
function($a, $stack){
return $stack($a . " 2 ");
},
function($a, $stack){
return $stack($a . " 3 ");
},
];
//foldl :: (b -> a -> b) -> b -> t a -> b
$result = foldLeft(function($stack,$item){
return function($pass) use ($stack, $item){
return call_user_func($item,$pass,$stack);
};
},function( $pass ){
return array("zero"=>$pass);
},array_reverse($arr));
$res = call_user_func($result,"finial value");
$this->assertEquals(["zero" => "finial value 1 2 3 "],$res);
}
具体来说就是对于$arr
中的函数,我们依次应用到finial value
后,最终调用function( $pass ){ return array("zero"=>$pass);}
。
看完上面的例子后,我们再来看下Laravel中Pipeline的实现,就会发现简单很多
4. Laravel中Pipeline实现
public function then(Closure $destination)
{
//!! 返回一个闭包,参数为 $passable=$request
$firstSlice = $this->getInitialSlice($destination);
//!! 翻转数组,后来的条件先调用
$pipes = array_reverse($this->pipes);
return call_user_func(
//!! 函数是编程,以$firstSlice为初始值,对$pipes挨个调用$this->getSlice()
//!! $this->getSlice()返回的函数接受两个参数($stack, $pipe)
//!! 其中$pipe是$pipes中的单个函数,$stack是$this->getSlice()函数处理完后的值
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
);
}··
上面的getInitialSlice
函数即我们的$zero
,而getSlice
则是(b -> a -> b)
,具体两个函数的实现可以去看下Laravel,原理就是第3节讲的。
5. 总结
Pipeline可以方便我们应用一系列中间函数到要处理的对象,如果其中某一个中间处理函数失败,我们可以抛出异常,我们可以看到Laravel中好多地方都用到了Pipeline,是非常基础的功能。