草稿 3pipeline
laravel 5.1 的pipeline
pipeline是在启动过程中的哪个环节这个得需要知道。
其实是在$kernel->handle($request);这部分中handle方法里面。handle方法就是捕获请求。它做了两个事情(其他的还没看到)。1、注册服务提供者。2、请求经过全局中间件。
这里只谈第二个 请求经过全局中间件。
Illuminate\Foundation\Http\kernel.php
public function handle($request){
$this->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
//省略捕获异常…
return $response;
}
protected function sendRequestThroughRouter()
{
//把请求实例绑定到用单例模式容器,array_reduce()的初始参数里面也写
$this->app->instance('request', $request);
// 清除已完成的请求
Facade::clearResolveInstance('request');
// 启动服务提供者,单独的一个篇章。
$this->bootstrap();
// 管道
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware()?[]:$this->middleware)
->then($this->dispatchToRouter());
}
//***就是这里
protected function dispachToRouter()
{
return function($request) {
//当前请求实例已经通过上面定义好的中间件,也就是说已经经过了(through)全局中间件。要说之与前的instance('request',$request) 的区别一个是没有经过中间件,这里是经过了中间件的情况的单例,具体我也不是太清楚。
$this->app->instance('request', $request);
// 这里就是分配给路由了,其他是请就路由的部分的功能。
return $this->router->dispatch($request);
};
}
Illuminate\Pipeline\Pipeline.php(自己写的就是练习)
// 设置$request
public function send($possible)
{
$this->possible = $possible;
return $this;
}
// 设置中间件
public function through($pipes)
{
$this->pipes = is_array($pipes)?$pipes:fun_get_args();
return $this;
}
// 运行包和穿透中间件操作
public function then($destination)
{
$firstSlice = $this->firstSlice($destination);
$pipes = array_reverse($this->pipes);
return call_user_func(
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->possible
);
}
public function firstSlice($destination)
{
return function($request) use ($destination){
return $destination($request);
};
}
public function getSlice()
{
return function ($stack, $pipe) {
//这个是包洋葱时候用到的
return function($request) use ($stack, $pipe){
// ...code ,其实就是要变成下面的这样形式,这个是穿透洋葱的过程
// return $pipe::handle($stack);
};
};
}
Illuminate\Pipeline\Pipeline.php; laravel的pipeline
// kernel里面new Pipeline($this->app)时候用到
public function __construct(Container $container)
{
$this->container = $container;
}
//设置request对象
public function send($passable)
{
$this->passable = $passable;
return $this;
}
// 设置中间件
public function through($pipes)
{
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
// 这里用到了包洋葱过程和穿透洋葱过程
public function then(Closure $destination)
{
$firstSlice = $this->getInitialSlice($destination);
$pipes = array_reverse($this->pipes);
return call_user_func(
// 这里面关键的两个地方,一个是array_reduce,看文档自行理解(看社区文档laravel之道理解其用法)。另一个$this->getSlice()。关键点看getSlice方法的返回值!返回值!返回值!。
array_reduce($pipes, $this->getSlice(), $firstSlice), $this->passable
);
}
// 第一次看的时候一头雾水,因为当时用匿名函数没多久也熟悉。匿名函数一个特性是懒惰加载,只有调用的时候才会执行
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));
// handle方法
// 其实就是$this->container->make($name)->{$this->method}($passable,$stack);
}
};
};
}
protected function getInitialSlice(Closure $destination)
{
return function ($passable) use ($destination) {
//其实就是$destination($passable);
return call_user_func($destination, $passable);
};
}
- 上面提到的包洋葱和穿透洋葱过程请点击这里
这里简单记下包洋葱和穿透洋葱的过程
分析包洋葱皮儿过程,下面的方法都是简写
// 设定一组中间件
$middleware = ['A', 'B', 'C'];
// 设置array_reduce第一个处理的参数即洋葱核心,即下面例子中处理的$fisrtSlice可不要以为是$middleware中的C。
$firstSlice = $this->getInitialSlice($destination);
// 源码then方法中的array_reduce()用法,包洋葱过程其实就是array_reduce干的事情。
array_reduce(array_reverse($middleware), $this->getSlise(), $firstSlise);
//看返回值!返回值!返回值!
protected function getInitialSlice(Closure $destination)
{
return function($request) use ($destination){
return $destination($request);
};
}
// 简写:
protected function getSlice(){
return function($stack, $pipe){
//看到request是不是会头痛。看下面的例子
return function($request) use ($stack, $pipe)
{
// code 这里其实不用关心,因为现在只是包洋葱的过程。
};
};
}
另一种写法
// array_reduce(array_reverse($middleware), getSlise(), $firstSlise);
array_reduce(
['C', 'B', 'A'],
// 每个元素要执行的函数
function($stack, $pipe){
//看到request是不是会头痛。看下面的例子
return function($request) use ($stack, $pipe)
{
// code 这里其实不用关心,因为现在只是包洋葱的过程。
}
},
// 第一个参数,如果没有返回值则作为返回值返回。相当于数组变成了['第一个参数','C', 'B', 'A'], 并且也是洋葱和核心。
function($request) use ($destination){
return $destination($request);
}
);
把array_reduce理解为用函数getSlice()循环执行数组$middleware的元素。每次循环完以后给匿名函数赋个别名,方便理解。
第一次循环C1 => function($request) use ($firstSlice, 'C'){}
第二次循环B1 => function($request) use (C1, 'B'){}
第三次循环A1 => function($request) use (B1, 'A'){}
最后返回的是A1也是一个匿名函数,包洋葱这个过程这里就结束了,下面是穿透洋葱的过程。
穿透(剥)洋葱皮儿,主要用到几个函数。
// 第一个函数
//就执行了call_user_func(),作用是调用匿名函数。第一个参数是要调用的匿名函数,其他参数都是传给这个匿名函数的参数。
call_user_func()
//第二个函数
protected function getSlice()
{
return function ($stack, $pipe) {
// 包的过程中到这里就结束了。穿透过程中是执行下面的代码
return function ($passable) use ($stack, $pipe) {
// 判断是否是匿名(回调)函数,这里只有$firstSlice是回调函数
if ($pipe instanceof Closure) {
return call_user_func($pipe, $passable, $stack);
} else {
// 这是获取中间件名称和后面的参数,参数具体效果我还不知道… - -!!
list($name, $parameters) = $this->parsePipeString($pipe);
// 实例话中间件,并调用handle方法。
// 传参数$passable:请求实例。其实是传过来的$request
// 传参数$stack 匿名(回调)函数 。上面说到的A1,B1,C1。
return call_user_func_array(
[$this->container->make($name), $this->method],
array_merge([$passable, $stack], $parameters)
);
}
};
};
}
// 第三个函数
// 如果你在laravel里面注册过中间件都会见到这个代码
public function handle($request, Closure $next)
{
//中间件自己的code...
return $next($request);
}
穿透(剥)皮儿过程
上面包洋葱皮最后得到的结果是A1。 就可以理解成call_user_func(A1, $request)
,$request就是$this->passable。执行A1也就是执行function($request) use (B1, 'A'){}
这块代码。具体是什么请看下面
// 这是getSlice()方法的一部分。上面已经注释过了所这里就每天家注释
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)
);
}
};
接着A1的过程,判断A是不是匿名(回调)函数。明显不是,走下面。实例话中间件A并调用里面的handle方法。类似A->handle($request, $stack)。并且handle返回$stack($request);这就可以理解为B1($request),接着C1($request),接着$firstSlice($request)。不过要注意:$firstSlice($request)是什么?它不是洋葱皮而是核心。所以走自己的代码。$destination($request)
->function($request){$this->app->instance('request', $request); return $this->router->dispatch($request);}
结束。
这里是下面,直接调用$this->getSlice()返回什么?
// getSlice()的返回值,就是下面这段,可以直接把这段匿名函数代替调用getSlice()的地方。
function ($stack, $pipe){
return function($request) use ($stack, $pipe)
{
//code...
}
}
// 把这个匿名函数赋给一个变量,会变成什么样子?$callback()结果是什么?
$callback = function ($stack, $pipe){
return function($request) use ($stack, $pipe)
{
//code...
}
}
//$callback() -> 是下面函数的结果。
function($request) use ($stack, $pipe){//code...}
本作品采用《CC 协议》,转载必须注明作者和本文链接