Skip to main content

路由调度之聚合中间件

简介

本篇重点剖析路由中间件如何聚合在一起

聚合概念:从各个路由中间件配置位置合在一起,再从合在一起的单词标识获取中间件类名

主要讲明:

  • 实现中间件聚合逻辑步骤
  • 主要从哪几个中间件配置中聚合起来
  • 控制器的实例化

逻辑简介

  • 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);
    }