前言
在這篇文章之前,我已經寫過一系列關於Laravel的文章了,我覺得他們對於你理解Laravel的整個程式碼結構來說,具有非常重要的意義,今天我們們繼續分析,今天的主角是Pipeline,也就是俗稱的管道,程式碼我已經上傳至碼雲:php-pipeline,程式碼量並不大,但是請仔細閱讀,可能並不好理解,可能有點兒短小精悍的意思吧。
管道
管道在Laravel中扮演了一個極其重要的角色,因為你所有的中介軟體都是通過管道進行操作的,想要理解中介軟體,必須得理解管道,laravel的管道檔案位於vendor/laravel/framework/src/illuminate/Pipeline目錄下面:
程式碼並不多,但是為了給大家講清楚,我自己實現了一個精簡版,上面已經說過了,已經上傳到了碼雲,核心程式碼16行,哈哈,是不是很驚訝!
希望大家一定要把程式碼下載下來,這樣更有助於理解。
function Pipeline($stack, $pipe)
{
return function ($passable) use ($stack, $pipe) {
if (is_callable($pipe)) {
$pipe($passable, $stack);
} elseif (is_object($pipe)) {
$method = "handle";
if (!method_exists($pipe, $method)) {
throw new InvalidArgumentException('object that own handle method');
} else {
$pipe->$method($passable, $stack);
}
} else {
throw new InvalidArgumentException('$pipe must be callback or object');
}
};
}
上面這個就是它的真面目了,下面我們先來看一下如果使用它:
interface TestUnit
{
public function handle($passable, callable $next = null);
}
class Unit1 implements TestUnit
{
public function handle($passable, callable $next = null)
{
echo __CLASS__ . '->' . __METHOD__ . " called\n";
$next($passable);
}
}
class Unit2 implements TestUnit
{
public function handle($passable, callable $next = null)
{
echo __CLASS__ . '->' . __METHOD__ . " called\n";
$next($passable);
}
}
class Unit3 implements TestUnit
{
public function handle($passable, callable $next = null)
{
echo __CLASS__ . '->' . __METHOD__ . " called\n";
$next($passable);
}
}
class InitialValue implements TestUnit
{
public function handle($passable, callable $next = null)
{
echo __CLASS__ . '->' . __METHOD__ . " called\n";
//$next($passable);
}
}
$pipeline = array_reduce([new Unit1(), new Unit2(), new Unit3()], "Pipeline", function ($passable) {
(new InitialValue())->handle($passable);
});
$pipeline(1);
上面我貼出如何使用Pipeline的例子,接下來的講解就以這個為例,不然很不好理解,在上面的測試程式碼裡面,我宣告瞭一個TestUnit介面,這個介面只有一個方法,就是handle,然後宣告瞭三個實現TestUnit介面的類,分別是Unit1,Unit2,Unit3,它們的handle方法很簡單:
echo __CLASS__ . '->' . __METHOD__ . " called\n";
$next($passable);
在PHP中__CLASS__
指代當前呼叫呼叫方法所屬的類,__METHOD__
指代當前呼叫的方法,如果我們執行上面的程式碼,會得到下面的結果:
E:\php-pipeline>php debug.php
Unit3->Unit3::handle called
Unit2->Unit2::handle called
Unit1->Unit1::handle called
InitialValue->InitialValue::handle called
控制檯結果和我預想的結果保持一致,舒服!
在講解上面的程式碼之前,你一定要知道如何使用array_reduce
方法,如果你不知道,也沒關係,可以參考php的官方文件:array_reduce,使用很簡單,我就不多說了。
在上面的程式碼中,我們看是如何是如何使用array_reduce的:
$pipeline = array_reduce([new Unit1(), new Unit2(), new Unit3()], "Pipeline", function ($passable) {
(new InitialValue())->handle($passable);
});
array_reduce
遍歷的是有三個元素組成的陣列,分別是Unit1,Unit2,Unit3類的例項物件,回撥方法是是Pipeline
,也就是我們上面貼出來的函式,初始值為:
function ($passable) {
(new InitialValue())->handle($passable);
}
注意了,它是一個回撥方法,我們把它簡稱為$init
,這個一定要記住。
好了,不多說了,我們開始遍歷[new Unit1(), new Unit2(), new Unit3()]
這個陣列。
遍歷new Unit1()
,此時我們看呼叫Pipeline的返回結果,是一個回撥方法,對不對?為了後面的分析,我們把這個會調記為如下形式(你可以理解為虛擬碼):
$c1=callback($passable)[$init,new Unit1()]
同樣的,我們遍歷new Unit2()
,得到的回撥簽名如下:
$c2=callback($passable)[$c1,new Unit2()]
最後遍歷new Unit3()
,虛擬碼如下:
$c3=callback($passable)[$c2,new Unit3()]
通過上面的測試程式碼,我們知道array_reduce返回的是$c3
這個回撥方法,也就是賦值給了$pipeline
變數,接下來呼叫c3
這個回撥,如下:
$pipeline(1);
這裡為了方便,我們把引數設定為1,實際上你可以設定為任何值,這個沒關係,我們看到當呼叫c3
的時候,這個時候,我們應該回到Pipeline
這個函式的本體了,引數$passable
為1,$stack為c2
,pipe
為new Unit3()
,此時Unit3
的handle
方法被呼叫,如下:
public function handle($passable, callable $next = null)
{
echo __CLASS__ . '->' . __METHOD__ . " called\n";
$next($passable);
}
$passable
為1,$next
就是c2
,c2
在Unit3
的handle
方法中被呼叫了,那麼呼叫c2
的時候呢?$passable
為1,$stack
為c1
,Unit2
的handle
和Unit3
的handle
方法是一模一樣的,所以此時c1
被呼叫,c1
被呼叫的時候$passable
為1,$stack
為init
,init
就是這個啊:
function ($passable) {
(new InitialValue())->handle($passable);
}
是不是感覺說清楚了,也很簡單啊?
上面就是整個Laravel管道的精髓了,希望大家能夠理解。
總結
還是那句話,要想能力比別人強,必須付出異於常人的毅力。歡迎大家加入下面的qq群,平時可以多多交流: