🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 说明 上一篇分析了应用的初始化,也就是对`Http`类的`run()`方法里面调用的`runWithRequest ()`方法的第一行代码`$this->initialize()`的展开分析。让我们再看一眼`runWithRequest ()`方法的前几行: ``` protected function runWithRequest(Request $request) { $this->initialize(); // 加载全局中间件 $this->loadMiddleware(); . . . ``` 应用初始化后,接下来开始处理全局中间件。 # 中间件类的初始化 `loadMiddleware`方法: ``` protected function loadMiddleware(): void { if (is_file($this->app->getBasePath() . 'middleware.php')) { $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php'); } } ``` 依然是百用不厌的套路,通过`$this->app->middleware`来实例化中间件并获取其实例。 # 导入中间件 通过`$this->app->middleware`得到`Middleware`类的实例后,接着程序调用`import`方法,传入从「app」目录下的「middleware.php」文件中读取的数据。该文件的原始内容如下(原来全部注释掉的): ``` return [ // 全局请求缓存 // \think\middleware\CheckRequestCache::class, // 多语言加载 \think\middleware\LoadLangPack::class, // Session初始化 // \think\middleware\SessionInit::class, // 页面Trace调试 \think\middleware\TraceDebug::class, ]; ``` 这里为了研究中间件是如何加载的,先去掉两个注释,也就是添加两个中间件。接下来看`import`方法: ``` public function import(array $middlewares = [], string $type = 'global'): void { foreach ($middlewares as $middleware) { $this->add($middleware, $type); } } ``` 该方法传入一个中间件的数组和一个中间件类型,默认为`global`,关键是里面的`add`方法。跳到`add`方法: ``` public function add($middleware, string $type = 'route'): void { if (is_null($middleware)) { return; } $middleware = $this->buildMiddleware($middleware, $type); if ($middleware) { $this->queue[$type][] = $middleware; // 去除重复 $this->queue[$type] = array_unique($this->queue[$type], SORT_REGULAR); } } ``` 实际上真正干活的是`buildMiddleware`方法,直接前往: ``` protected function buildMiddleware($middleware, string $type): array { // 是否是数组 if (is_array($middleware)) { // 列出中间件及其参数 // 这里说明我们可以给中间件传入参数,且形式为 [中间件, 参数] list($middleware, $param) = $middleware; } // 是否是一个闭包 // 说明中间件可以是一个闭包 if ($middleware instanceof \Closure) { //返回闭包和参数 return [$middleware, $param ?? null]; } // 排除了上面几种类型,且不是字符串,抛出错误 if (!is_string($middleware)) { throw new InvalidArgumentException('The middleware is invalid'); } //中间件别名检查 $alias = $this->app->config->get('middleware.alias', []); if (isset($alias[$middleware])) { $middleware = $alias[$middleware]; } //如果中间件有包含中间件(说明中间件可以嵌套) //再走一遍「import」递归解析 if (is_array($middleware)) { $this->import($middleware, $type); return []; } //返回解析结果 return [[$middleware, 'handle'], $param ?? null]; } ``` 详细分析见以上代码注释。最后返回的结果,在`add`方法中,执行`$this->queue[$type][] = $middleware;`添加到一个队列。最终的解析结果大概是这样的: ![](https://img.kancloud.cn/46/38/46381409c576af978dc3a0ee6f7f9375_449x404.PNG) 至此,全局中间件就加载完毕。