Laravel 中间件解析
程序员文章站
2022-05-19 15:01:48
...
Laravel 中间件
代码展示
protected function sendRequestThroughRouter($request)
{
# $this->app->instance('request', $request);
# Facade::clearResolvedInstance('request');
# $this->bootstrap();
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter()); // $this->dispatchToRouter(),后期继续
}
new \Illuminate\Routing\Pipeline($this->app):
public function __construct(Container $container = null)
{
$this->container = $container;
}
public function send($passable)
{
$this->passable = $passable;
return $this;
}
public function through($pipes)
{
####################################################################################
# $this->middleware = [ #
# \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, #
# \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class, #
# \App\Http\Middleware\TrimStrings::class, #
# \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class, #
# ]; #
####################################################################################
$this->pipes = is_array($pipes) ? $pipes : func_get_args();
return $this;
}
// 中间件的本质
public function then(Closure $destination)
{
$pipeline = array_reduce(
array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
);
// 上面操作完之后返回一个匿名函数,接受一个参数
return $pipeline($this->passable);
}
// 注意这个是子类里面会调用的父carry,区别在于子类加入了异常处理
protected function carry()
{
// 接受匿名函数$stack参数和$pipe参数,返回匿名函数,再将此匿名函数作为第一个参数$stack迭代传入。
return function ($stack, $pipe) {
return function ($passable) use ($stack, $pipe) {
if ($pipe instanceof Closure) {
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
list($name, $parameters) = $this->parsePipeString($pipe);
$pipe = $this->getContainer()->make($name);
$parameters = array_merge([$passable, $stack], $parameters);
} else {
$parameters = [$passable, $stack];
}
// 中间件作用是提供了一种方便的机制来过滤进入应用的 HTTP 请求
// $this->method默认为handle,可通过via方法进行设置
return $pipe->{$this->method}(...$parameters);
};
};
}
protected function parsePipeString($pipe)
{
// 不带参数的pipe(class)或带参数(参数以,分割)的pipe(class:param1,param2,param3...)
list($name, $parameters) = array_pad(explode(':', $pipe, 2), 2, []);
if (is_string($parameters)) {
$parameters = explode(',', $parameters);
}
// $parameters为[]数组或[param1,param2,param3...]
return [$name, $parameters];
}
protected function prepareDestination(Closure $destination)
{
return function ($passable) use ($destination) {
return $destination($passable);
};
}
前置条件
array_reduce接受三个参数,第一个参数接收数组,第二个参数函数名(也可以是匿名函数,函数有两个参数,分别代表$result和$item),第三个参数(可选),该参数将被当成是数组中的第一个值来处理,或者如果数组为空的话就作为最终返回值。
匿名函数也叫闭包函数(closures),允许临时创建一个没有指定名称的函数,通过 Closure 类来实现的。
当对闭包函数进行赋值时,PHP 便会自动将此种表达式转换成内置类 Closure 的对象对象在进行赋值。
当闭包函数使用到 use 使用外部数据时,会在 Closure 对象生成一个 static 属性数组进行存放。
当闭包函数使用到参数时,会在 Closure 对象生成一个 parameter 属性数组进行存放。
public function then(Closure $destination)
{
$pipeline = array_reduce(
array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
);
return $pipeline($this->passable);
}
生成最终匿名函数的过程:
array_reduce执行第一次时得到如下简化的匿名函数返回,将会继续作为第一个参数进行迭代:
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#1 (0) { // $this->prepareDestination($destination)
}
["pipe"]=>
string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
}
}
第二次:
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#1 (0) { // $this->prepareDestination($destination)
}
["pipe"]=>
string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
}
}
["pipe"]=>
string(15) "App\Http\Middleware\TrimStrings"
}
}
第三次:
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#id (1) {
["static"]=>
array(2) {
["stack"]=>
object(Closure)#1 (0) { // $this->prepareDestination($destination)
}
["pipe"]=>
string(15) "Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull"
}
}
["pipe"]=>
string(15) "App\Http\Middleware\TrimStrings"
}
}
["pipe"]=>
string(15) "Illuminate\Foundation\Http\Middleware\ValidatePostSize"
}
}
依次类推,最终得到一个匿名函数如下(接受一个参数,此匿名函数内部使用上面递归形式的stack)。
function ($passable) {
if ($pipe instanceof Closure) {
return $pipe($passable, $stack);
} elseif (! is_object($pipe)) {
list($name, $parameters) = $this->parsePipeString($pipe);
$pipe = $this->getContainer()->make($name); // 实例化middleware
$parameters = array_merge([$passable, $stack], $parameters);
} else {
$parameters = [$passable, $stack];
}
return $pipe->{$this->method}(...$parameters); // 语法糖模式,因为middleware参数可有可无
};
最终匿名函数的调用过程(从最外层开始,这就是前面为什么要array_reverse,一层一层往里拨,整体上的处理是:pipe->handle)
最外层
($pipe=Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode):
(new \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode)->handle($passable, $stack)
执行完前置操作后,调用$stack($passable),继续进行下一层
下一层($pipe=Illuminate\Foundation\Http\Middleware\ValidatePostSize):
(new \Illuminate\Foundation\Http\Middleware\ValidatePostSize)->handle($passable, $stack)
执行完前置操作后,调用$stack($passable),继续进行下一层
**以此类推,当每层中间件的前置任务全部完成,即递归执行到最里面一层(路由分发,解析请求,返回响
应),再由最内层一层一层往回走,执行每层中间件的后置任务。至此,返回本次请求的响应。**
最内层(即路由分发,解析请求,返回响应)的操作代码展示,后续分析:即执行passable)匿名函数,位于下面方法中。
protected function prepareDestination(Closure $destination)
{
return function ($passable) use ($destination) {
return $destination($passable);
};
}
$destination:
protected function dispatchToRouter()
{
return function ($request) {
$this->app->instance('request', $request);
return $this->router->dispatch($request);
};
}
说明:所有的后置操作,都是执行到最内层,递归往回走时才会执行逐层执行。
上一篇: python学习笔记——字典