[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)
至此,全局中间件就加载完毕。