本文目录导读:

在 PHP 中,中间件的执行通常遵循 洋葱模型(Onion Model) 或 管道模式(Pipeline Pattern),核心思想是:请求依次通过中间件层,每一层可以决定是否继续传递请求(或处理响应返回)。
下面我会从基础概念到手写实现、再到主流框架(Laravel/ThinkPHP)的对比来说明。
核心执行机制:闭包传递
绝大多数 PHP 中间件的核心代码只有一句话:
// 伪代码核心: next 是一个回调,指向下一个中间件或最终处理 return $next($request);
- 当一个中间件调用
$next($request)时,程序会进入下一个中间件。 - 当所有中间件都执行完后,最终处理(路由/控制器)返回响应,响应再原路返回经过每个中间件的后续代码。
手写一个简单的中间件运行机制
为了彻底搞懂,我们可以手动实现:
步骤 1:定义中间件接口
interface Middleware {
public function handle($request, \Closure $next);
}
步骤 2:实现几个中间件
class AuthMiddleware implements Middleware {
public function handle($request, \Closure $next) {
echo "Auth 中间件:开始\n";
// 条件不通过,直接返回,不再向下
// if(!$request->hasToken) return "403 未授权";
$response = $next($request);
echo "Auth 中间件:结束\n";
return $response;
}
}
class LogMiddleware implements Middleware {
public function handle($request, \Closure $next) {
echo "Log 中间件:开始\n";
$response = $next($request);
echo "Log 中间件:结束\n";
return $response;
}
}
步骤 3:构建管道执行
class Pipeline {
private $middlewares = [];
public function add(Middleware $middleware) {
$this->middlewares[] = $middleware;
}
public function run($request, \Closure $finalHandler) {
// 关键:从最后一个中间件往前构建闭包链
$stack = $finalHandler;
// 反转数组,从最后一个中间件开始包裹
foreach(array_reverse($this->middlewares) as $middleware) {
$stack = function($request) use ($middleware, $stack) {
return $middleware->handle($request, $stack);
};
}
// 执行最终构建的闭包
return $stack($request);
}
}
步骤 4:测试
$pipeline = new Pipeline();
$pipeline->add(new AuthMiddleware());
$pipeline->add(new LogMiddleware());
$request = "some request data";
$result = $pipeline->run($request, function($req) {
echo "--- 核心业务处理 ---\n";
return "处理结果";
});
echo "\n最终返回: " . $result;
输出结果:
Auth 中间件:开始
Log 中间件:开始
--- 核心业务处理 ---
Log 中间件:结束
Auth 中间件:结束
最终返回: 处理结果
顺序执行逻辑:
Auth -> Log -> 核心 -> Log -> Auth这完全符合洋葱模型的 先入后出(Stack) 特性。
主流框架的实现对比
Laravel 中间件
Laravel 使用 Symfony 的 HttpKernelInterface + 管道模式。
- 定义:
app/Http/Kernel.php中的$middleware和$routeMiddleware。 - 执行流程:
// 核心代码片段(简化)
$response = (new Pipeline($this->app))
->send($request)
->through($middleware)
->then(function ($request) use ($router) {
return $router->dispatch($request);
});
- 单个中间件:
class CheckAge {
public function handle($request, \Closure $next) {
if ($request->age < 18) {
return redirect('home'); // 中断
}
return $next($request); // 继续
}
}
Laravel 还支持中间件组(web/api)、Terminable Middleware(请求结束后执行清理)。
ThinkPHP 6 中间件
ThinkPHP的中间件机制类似,定义在 config/middleware.php。
- 定义:
class Hello {
public function handle($request, \Closure $next) {
// 前置操作
$response = $next($request);
// 后置操作
return $response;
}
}
- 调用时通过
think\Pipeline类实现,和手写原理基本一致。
中间件的常见应用场景(前端视角)
如果你的工作涉及 PHP 后端,中间件最常用在以下场景:
| 场景 | 中间件作用 | 原生/框架解决方案 |
|---|---|---|
| 用户认证 | 未登录跳转登录页 | auth中间件(Laravel)think\middleware\AllowCrossDomain |
| CORS跨域 | 添加 Access-Control-* 头 |
App\Http\Middleware\Cors |
| 限流 | 防止DDOS/刷接口 | throttle:60,1(60次/分钟) |
| 请求日志 | 记录所有API请求参数和时间 | 自定义中间件 |
| 响应压缩 | gzip | \App\Http\Middleware\CompressResponse |
执行流程总结
[请求 进来]
↓
中间件1 开始(前置代码)
↓ 调用 $next($request)
中间件2 开始(前置代码)
↓ 调用 $next($request)
...
最终控制器/业务处理 (返回响应)
...
↑ $response 返回
中间件2 结束(后置代码)
↑ $response 返回
中间件1 结束(后置代码)
↑
[响应 返回给客户端]
关键点:
$next($request)就像 “放行”按钮 —— 不调用就卡住(中断请求)。- 响应的后置处理代码的执行顺序和中间件添加顺序相反(栈的特性)。
- 框架通常用
Pipeline类将多个中间件压扁成一个闭包来依次执行。
PHP 中间件的核心思想就是 装饰器模式 + 闭包传递,通过把行为分层包装,实现了高度可解耦的请求处理流程。