路由调度之聚合中间件
简介
本篇重点剖析路由中间件如何聚合在一起
聚合概念:从各个路由中间件配置位置合在一起,再从合在一起的单词标识获取中间件类名
主要讲明:
- 实现中间件聚合逻辑步骤
- 主要从哪几个中间件配置中聚合起来
- 控制器的实例化
逻辑简介
- 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); }
本篇如有错误、不当或者需补充的内容,请各位同僚多提宝贵意见。