路由调度之聚合中间件
简介
本篇重点剖析路由中间件如何聚合在一起
聚合概念:从各个路由中间件配置位置合在一起,再从合在一起的单词标识获取中间件类名
主要讲明:
- 实现中间件聚合逻辑步骤
- 主要从哪几个中间件配置中聚合起来
- 控制器的实例化
逻辑简介
- 1、调用
gatherRouteMiddleware
方法,开始路由中间件聚合 - 2、在
gatherRouteMiddleware
方法中调用gatherMiddleware
方法,获取 路由中 和 控制器中 定义的中间件单词标识 - 3、在
gatherMiddleware
方法中调用middleware
方法,获取 路由中 定义的中间件单词标识 - 4、
middleware
方法中,主要从匹配好的 Route 对象中 action 属性的middleware
键获取中间件标识 - 5、回第4步,接着在
gatherMiddleware
方法中调用controllerMiddleware
方法,获取 控制器中 定义的中间件单词标识 - 6、在
controllerMiddleware
方法中调用isControllerAction
方法,检测 Route 对象中的 action 属性的uses
键的值是不是字符串。 - 7、如何检测到不是字符串,返回空数组,即 控制器中 不会设置中间件
- 8、如何检测到是字符串,则字符串一定是 控制器@动作
- 9、继续第 8 步,在
controllerMiddleware
方法中调用controllerDispatcher
获取 控制器调度器 对象 - 10、调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数
- 11、回到
controllerMiddleware
方法,调用 getController 方法,获取 控制器对象 - 12、检测 Route 对象的
controller
属性有没有绑定实例化的 控制器对象 - 13、如果没有绑定 控制器对象 则在
getController
方法中调用parseControllerCallback
方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素 - 14、接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件
- 15、回到第 12 步,如果绑定了 控制器对象,则直接返回 控制器对象
- 16、回到
controllerMiddleware
方法,调用 getControllerMethod 方法,获取 控制器方法名称 - 17、在
getControllerMethod
方法中调用parseControllerCallback
,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素 - 18、回到第 10 步,在 getMiddleware 方法中,检测 控制器对象 有没有 getMiddleware 方法
- 19、如果没有则返回空数组
- 20、如果有则调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识
- 21、回到第 3 步,调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去
- 22、回到第 2 步,利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名
- 23、在
gatherRouteMiddleware
方法中调用sortMiddleware
方法,对中间件进行排序,最终返回聚合好的中间件数组
逻辑详解
1、调用
gatherRouteMiddleware
方法,开始路由中间件聚合protected function runRouteWithinStack(Route $route, Request $request)
{
$shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
$this->container->make('middleware.disable') === true;
// 调用 `gatherRouteMiddleware` 方法,开始路由中间件聚合
$middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
return (new Pipeline($this->container))
->send($request)
->through($middleware)
->then(function ($request) use ($route) {
return $this->prepareResponse(
$request, $route->run()
);
});
}2、在
gatherRouteMiddleware
方法中调用gatherMiddleware
方法,获取 路由中 和 控制器中 定义的中间件单词标识public function gatherRouteMiddleware(Route $route)
{
// 调用 `gatherMiddleware` 方法,获取 路由中 和 控制器中 定义的中间件单词标识
$middleware = collect($route->gatherMiddleware())->map(function ($name) {
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
})->flatten();
return $this->sortMiddleware($middleware);
}3、在
gatherMiddleware
方法中调用middleware
方法,获取 路由中 定义的中间件单词标识public function gatherMiddleware()
{
// 检测中间件是否聚合过
if (! is_null($this->computedMiddleware)) {
return $this->computedMiddleware;
}
$this->computedMiddleware = [];
return $this->computedMiddleware = array_unique(array_merge(
// 没有聚合过,调用 `middleware` 方法,获取 路由中 定义的中间件单词标识
$this->middleware(), $this->controllerMiddleware()
), SORT_REGULAR);
}4、
middleware
方法中,主要从匹配好的 Route 对象中 action 属性的middleware
键获取中间件标识public function middleware($middleware = null)
{
if (is_null($middleware)) {
// 从匹配好的 Route 对象中 action 属性的 `middleware` 键获取中间件标识
return (array) ($this->action['middleware'] ?? []);
}
if (is_string($middleware)) {
$middleware = func_get_args();
}
$this->action['middleware'] = array_merge(
(array) ($this->action['middleware'] ?? []), $middleware
);
return $this;
}5、回第4步,接着在
gatherMiddleware
方法中调用controllerMiddleware
方法,获取 控制器中 定义的中间件单词标识public function gatherMiddleware()
{
if (! is_null($this->computedMiddleware)) {
return $this->computedMiddleware;
}
$this->computedMiddleware = [];
return $this->computedMiddleware = array_unique(array_merge(
// 调用 `controllerMiddleware` 方法,获取 控制器中 定义的中间件单词标识
$this->middleware(), $this->controllerMiddleware()
), SORT_REGULAR);
}6、在
controllerMiddleware
方法中调用isControllerAction
方法,检测 Route 对象中的 action 属性的uses
键的值是不是字符串7、如何检测到不是字符串,返回空数组,即 控制器中 不会设置中间件
8、如何检测到是字符串,则字符串一定是 控制器@动作
9、继续第 8 步,在
controllerMiddleware
方法中调用controllerDispatcher
获取 控制器调度器 对象10、调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数
11、回到
controllerMiddleware
方法,调用 getController 方法,获取 控制器对象public function controllerMiddleware()
{
// 检测 Route 对象中的 action 属性的 `uses` 键的值是不是字符串
if (! $this->isControllerAction()) {
// 如何检测到不是字符串,返回空数组,即 控制器中 不会设置中间件
return [];
}
// 调用 `controllerDispatcher` 获取 控制器调度器 对象
// 调用 控制器调度器 对象中的 getMiddleware 方法,以 控制器对象 和 方法名 为参数
return $this->controllerDispatcher()->getMiddleware(
// 调用 getController 方法,获取 控制器对象
$this->getController(), $this->getControllerMethod()
);
}12、检测 Route 对象的
controller
属性有没有绑定实例化的 控制器对象13、如果没有绑定 控制器对象 则在
getController
方法中调用parseControllerCallback
方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素14、接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件
15、回到第 12 步,如果绑定了 控制器对象,则直接返回 控制器对象
public function getController()
{
// 检测 Route 对象的 `controller` 属性有没有绑定实例化的 控制器对象
if (! $this->controller) {
// 在 `getController` 方法中调用 `parseControllerCallback` 方法,以 @ 为分隔符,分割 控制器@方法 字符串,取第一个数组元素
$class = $this->parseControllerCallback()[0];
// 接着调用 Laravel 容器的 make 方法,开始实例化 控制器对象,最后返回 控制器对象,记住,这里的实例化,会调用 控制器的构造方法,注册 控制器中 的中间件
$this->controller = $this->container->make(ltrim($class, '\\'));
}
// 返回 控制器对象
return $this->controller;
}16、回到
controllerMiddleware
方法,调用 getControllerMethod 方法,获取 控制器方法名称public function controllerMiddleware()
{
if (! $this->isControllerAction()) {
return [];
}
return $this->controllerDispatcher()->getMiddleware(
// 调用 getControllerMethod 方法,获取 控制器方法名称
$this->getController(), $this->getControllerMethod()
);
}17、在
getControllerMethod
方法中调用parseControllerCallback
,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素protected function getControllerMethod()
{
// 调用 `parseControllerCallback` ,同样以 @ 为分割符,分割 控制器@方法 字符串,取第二个数组元素
return $this->parseControllerCallback()[1];
}18、回到第 10 步,在 getMiddleware 方法中,检测 控制器对象 有没有 getMiddleware 方法
19、如果没有则返回空数组
20、如果有则调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识
public function getMiddleware($controller, $method)
{
// 检测 控制器对象 有没有 getMiddleware 方法
if (! method_exists($controller, 'getMiddleware')) {
return [];
}
// 调用 控制器对象 中的 getMiddleware 方法,获取第 14 步注册好的中间件单词标识,然后通过集合的 reject 方法过滤掉 except 指定方法,最后返回过滤好的 中间件单词标识
return collect($controller->getMiddleware())->reject(function ($data) use ($method) {
return static::methodExcludedByOptions($method, $data['options']);
})->pluck('middleware')->all();
}21、回到第 3 步,调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去
public function gatherMiddleware()
{
if (! is_null($this->computedMiddleware)) {
return $this->computedMiddleware;
}
$this->computedMiddleware = [];
// 调用内置 array_unique 和 array_merge 方法,合并 路由中 和 控制器中 配置的中间件单词标识,并去重,最后返回去
return $this->computedMiddleware = array_unique(array_merge(
$this->middleware(), $this->controllerMiddleware()
), SORT_REGULAR);
}22、回到第 2 步,利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名
23、在
gatherRouteMiddleware
方法中调用sortMiddleware
方法,对中间件进行排序,最终返回聚合好的中间件数组public function gatherRouteMiddleware(Route $route)
{
// 利用集合的 map 方法与 MiddlewareNameResolver 类的 resolve 方法,将中间件单词标识变换成中间件类名
$middleware = collect($route->gatherMiddleware())->map(function ($name) {
return (array) MiddlewareNameResolver::resolve($name, $this->middleware, $this->middlewareGroups);
})->flatten();
// 在 `gatherRouteMiddleware` 方法中调用 `sortMiddleware` 方法,对中间件进行排序,最终返回聚合好的中间件数组
return $this->sortMiddleware($middleware);
}