laravel實現了管道機制, 即上一個中間件的輸出是下一個中間件的輸入,是對裝飾器模式的成功應用。
閉包
有必要講下閉包
1.閉包做參數傳遞
先來看一段代碼
class A {
function go($next){
echo "step a".'<br/>';
return $next;
}
}
class B{
function go($next){
echo "step b".'<br/>';
return $next;
}
}
class C{
function go($next){
echo "step c".'<br/>';
return $next;
}
}
$fn();
(new C)->go(
(new B)->go(
(new A())->go($fn)
)
);
運行一下,結果如下
獲取請求
step a
step b
step c
2.優化一下
<?php
$fn = function(){
echo "獲取請求".'<br/>';
};
class A {
function go($next){
echo "step a".'<br/>';
$next();
}
}
class B{
function go($next){
echo "step b".'<br/>';
$next();
}
}
class C{
function go($next){
echo "step c".'<br/>';
$next();
}
}
function getSlices($next,$step){
// var_dump($next);
// var_dump($step);
return function()use($next,$step){
return (new $step)->go($next);
};
}
$steps = [
'A','B','C'
];
call_user_func(
array_reduce($steps,'getSlices',$fn)
);
運行一下 返回了
step c
step b
step a
獲取請求
結果正好倒過來了
3.分析下結果
第一步
$result_1 = function(){
return function()use($fn,"A"){
return (new "A")->go($fn);
};
}
第二步
$result_2 = function(){
return function()use($result_1 ,"B"){
return (new "B")->go($result_1 );
};
}
第三步
$result_3 = function(){
return function()use($result_2,"C"){
return (new C)->go($result_2);
};
}
最後
call_user_func($result_3);
最外層是(new C)->go($result_2)
,所以先輸出了step c
,然後纔是內層的step b
和step a
.
Illuminate\Foundation\Http\Kernel
1.handle()
前面做了一些 bootstrap 的工作,核心是sendRequestThroughRouter()
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Exception $e) {
//省略異常處理
}
$this->app['events']->fire('kernel.handled', [$request, $response]);
return $response;
}
2.sendRequestThroughRouter()
核心是最後一句
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
Illuminate\Pipeline\Pipeline
1.send()
接受http請求實例
public function send($passable){
$this->passable = $passable;
return $this;
}
2.through
設置需要處理的中間件
public function through($pipes){
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
3.then()
public function then(Closure $destination){
$firstSlice = $this->getInitialSlice($destination);
$pipes = array_reverse($this->pipes);
return call_user_func(
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
);
}
重點是getSlice()
4.getSlice()
是不是有點眼熟,但是這裏套了兩層閉包,上面的例子裏明明只套了一層閉包,這是爲什麼呢?
protected function getSlice(){
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
if ($pipe instanceof Closure) {
return call_user_func($pipe, $passable, $stack);
} else {
list($name, $parameters) = $this->parsePipeString($pipe);
return call_user_func_array([$this->container->make($name), $this->method],array_merge([$passable, $stack], $parameters));
}
};
};
}
我們注意到,在最上面的例子中,array_reduce()
第二個參數使用了函數名字符串作爲參數,而這裏使用的是匿名函數。