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()
第二个参数使用了函数名字符串作为参数,而这里使用的是匿名函数。