本文目录导读:

在 PHP 中间件中,执行顺序遵循洋葱模型(Onion Model)或管道模型,理解这个顺序对于正确处理请求和响应至关重要。
核心执行原理
中间件的执行顺序是:先进先出(FIFO)处理请求,后进先出(LIFO)处理响应。
// 假设有中间件 A、B、C,注册顺序为 A → B → C // 执行流程如下: 请求 → A(前) → B(前) → C(前) → 控制器/核心逻辑 → C(后) → B(后) → A(后) → 响应
具体实现示例
Laravel 框架示例
// 在 Kernel.php 中
protected $middleware = [
\App\Http\Middleware\A::class,
\App\Http\Middleware\B::class,
\App\Http\Middleware\C::class,
];
// 每个中间件的 handle 方法
class A {
public function handle($request, Closure $next)
{
// 请求到达时的处理(前)
echo "A-开始 ";
// 传递给下一个中间件
$response = $next($request);
// 响应返回时的处理(后)
echo "A-结束 ";
return $response;
}
}
// 输出结果:
// A-开始 B-开始 C-开始 核心逻辑 C-结束 B-结束 A-结束
手动实现中间件管道
class MiddlewarePipeline
{
private $middlewares = [];
public function add($middleware)
{
$this->middlewares[] = $middleware;
}
public function handle($request)
{
// 使用 array_reduce 构建调用链
$core = function($request) {
echo "核心逻辑 ";
return "响应";
};
$pipeline = array_reduce(
array_reverse($this->middlewares),
function($next, $middleware) {
return function($request) use ($next, $middleware) {
return $middleware->handle($request, $next);
};
},
$core
);
return $pipeline($request);
}
}
// 使用示例
$pipeline = new MiddlewarePipeline();
$pipeline->add(new MiddlewareA());
$pipeline->add(new MiddlewareB());
$pipeline->add(new MiddlewareC());
$result = $pipeline->handle($request);
常见框架的执行顺序
Laravel 中间件优先级
// Laravel 中的执行顺序(从外到内) 1. Global Middleware(全局中间件) 2. Group Middleware(组中间件,如 web、api) 3. Route Middleware(路由中间件) 4. Controller Middleware(控制器中间件)
Slim 框架示例
// Slim 4 中间件执行顺序 $app->add(new MiddlewareA()); // 最先执行 $app->add(new MiddlewareB()); // 第二个执行 $app->add(new MiddlewareC()); // 第三个执行 // 实际执行顺序 // 请求 → A → B → C → 路由处理 → C → B → A → 响应
关键要点
-
注册顺序决定执行顺序:
- 先注册的先执行请求逻辑
- 后注册的先执行响应逻辑
-
终止中间件(Terminable):
// Laravel 中的终止中间件 class TerminateMiddleware { public function handle($request, Closure $next) { return $next($request); } // 响应发送后执行 public function terminate($request, $response) { // 日志记录、清理工作等 } } -
CSRF 保护的特殊性:
// CSRF 中间件通常位于 Web 中间件组的最外层 protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // CSRF 检查在 Session 之后 \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, ], ];
调试技巧
// 在中间件中添加日志
class DebugMiddleware
{
public function handle($request, Closure $next)
{
\Log::info('中间件开始: ' . get_class($this));
$response = $next($request);
\Log::info('中间件结束: ' . get_class($this));
return $response;
}
}
- 请求方向:按注册顺序执行
- 响应方向:按注册逆序执行
- 核心逻辑:位于所有中间件的最内层
理解这个执行顺序,可以帮助你更好地组织中间件逻辑,
- 认证中间件应该靠前,以便后续中间件知道用户身份
- 日志中间件可以在外层捕获所有请求
- 响应格式化中间件应该在最内层