1. 背景
在Laravel中經(jīng)常需要對一個對象,經(jīng)過多個中間層處理后,才到真正處理的函數(shù)舷暮,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
,一個常見的應(yīng)用是
$pipeline->send($request)->through($middles)->then($handle);
非常直觀的語義下面,在看實現(xiàn)之前复颈,我們先自己來實現(xiàn)下這個功能
3. 動手實現(xiàn)
在實現(xiàn)之前,我們先看下下面一段代碼
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
函數(shù)耗啦,實現(xiàn)了數(shù)組求和,函數(shù)foldLeft
有3個參數(shù)机杜,第一個是一個二元函數(shù)帜讲,第二個是一個初值,第三個則是需要操作的集合叉庐,組合起來可以通過一張圖來說明:
讓我們對foldLeft
函數(shù)更進一步舒帮,看下他的函數(shù)類型
foldLeft :: (b -> a -> b) -> b -> t a -> b
此處(b -> a -> b)
是函數(shù)$f
的類型,根據(jù)上面的定義陡叠,如果我們現(xiàn)在數(shù)組t a
中元素a
都是函數(shù)玩郊,而類型b
也是函數(shù),這樣子的話枉阵,此處a
相當于于處理對象的middle ware
译红,而b是具體的處理對象的函數(shù),這樣子講可能比較抽象兴溜,讓我們看下下面的例子:
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
中的函數(shù)侦厚,我們依次應(yīng)用到finial value
后,最終調(diào)用function( $pass ){ return array("zero"=>$pass);}
拙徽。
看完上面的例子后刨沦,我們再來看下Laravel中Pipeline的實現(xiàn),就會發(fā)現(xiàn)簡單很多
4. Laravel中Pipeline實現(xiàn)
public function then(Closure $destination)
{
//!! 返回一個閉包,參數(shù)為 $passable=$request
$firstSlice = $this->getInitialSlice($destination);
//!! 翻轉(zhuǎn)數(shù)組,后來的條件先調(diào)用
$pipes = array_reverse($this->pipes);
return call_user_func(
//!! 函數(shù)是編程,以$firstSlice為初始值,對$pipes挨個調(diào)用$this->getSlice()
//!! $this->getSlice()返回的函數(shù)接受兩個參數(shù)($stack, $pipe)
//!! 其中$pipe是$pipes中的單個函數(shù),$stack是$this->getSlice()函數(shù)處理完后的值
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
);
}··
上面的getInitialSlice
函數(shù)即我們的$zero
膘怕,而getSlice
則是(b -> a -> b)
想诅,具體兩個函數(shù)的實現(xiàn)可以去看下Laravel,原理就是第3節(jié)講的岛心。
5. 總結(jié)
Pipeline可以方便我們應(yīng)用一系列中間函數(shù)到要處理的對象来破,如果其中某一個中間處理函數(shù)失敗,我們可以拋出異常忘古,我們可以看到Laravel中好多地方都用到了Pipeline徘禁,是非常基礎(chǔ)的功能髓堪。