[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;`添加到一個隊列。最終的解析結果大概是這樣的:

至此,全局中間件就加載完畢。