PHP中间件本质是接收$request和$next、返回$response的函数链,核心在于调用顺序与短路控制;手写pipe调度器通过闭包递归实现洋葱模型,需统一解析请求体并避免提前输出。

PHP 中间件本质是函数链,不是类或框架专属概念
中间件在 PHP 里没有语言级支持,它只是对「请求→处理→响应」流程的函数式拆解。你不需要依赖 Laravel 或 Slim 才能写中间件——只要能接收 $request 和 $next,返回 $response,就是中间件。关键在于调用顺序和短路控制。
常见错误是把中间件写成独立类方法却忽略 $next 调用,导致后续逻辑不执行;或者在中间件里直接 echo 或 exit,破坏了响应统一出口。
- 必须接收两个参数:
$request(数组或对象)和$next(下一个中间件 callable) - 必须返回
$response(不能 void,否则链断裂) - 想终止流程?不调用
$next,直接 return 响应即可(如鉴权失败) - 想修改请求?改完
$request再传给$next($request)
手写一个可运行的中间件调度器(无框架)
核心就一个 pipe() 函数:把中间件列表压栈,从头开始调用,每个中间件决定是否继续往下走。不用 Composer、不依赖 PSR-7,纯 PHP 8.1+ 可跑。
// middleware.php
$middleware = [
function ($request, $next) {
echo "[LOG] Start\n";
$response = $next($request);
echo "[LOG] End\n";
return $response;
},
function ($request, $next) {
if (empty($request['token'])) {
return ['status' => 401, 'body' => 'Unauthorized'];
}
return $next($request);
},
function ($request, $next) {
return ['status' => 200, 'body' => 'OK'];
}
];
function pipe($middlewares, $initialRequest) {
$stack = array_reverse($middlewares);
$next = function ($req) use (&$stack, &$next) {
if (!$stack) return $req;
$mw = array_pop($stack);
return $mw($req, $next);
};
return $next($initialRequest);
}
$result = pipe($middleware, ['token' => 'abc']);
print_r($result); // 输出含日志和 OK 的响应
注意:array_reverse() 是为了让第一个注册的中间件最先执行(符合直觉),但实际调度逻辑靠闭包递归维持「洋葱模型」。
中间件里怎么安全读取 HTTP 请求数据?
原生 PHP 没有标准 Request 对象,$_GET、$_POST、php://input 都得你自己封装。别在每个中间件里重复解析——提前统一提取,作为 $request 数组传入。
- GET 参数:直接用
$_GET,但需过滤(filter_input(INPUT_GET, ...)) - POST 表单:优先用
$_POST,但注意Content-Type: application/json时它为空 - JSON 请求体:用
json_decode(file_get_contents('php://input'), true),并检查json_last_error() - Header:用
getallheaders()(CLI 不可用)或$_SERVER['HTTP_X_TOKEN'](下划线转横线)
建议封装一个 parse_request() 函数,在调度前调用一次,避免中间件各自解析出错或重复 IO。
为什么有些中间件总不生效?检查这三点
最常卡住的地方不是逻辑,而是执行时机和数据流向。尤其在 Apache/Nginx + PHP-FPM 环境下,容易误判。
-
$next没被调用:比如鉴权中间件里写了die('no')而不是return ['status'=>403],整个链就断了 - 请求体已被读取过一次:
php://input是只读流,中间件 A 读完后,中间件 B 再读就是空字符串 —— 必须缓存或共享解析结果 - Response 被提前输出:中间件里用了
echo、header()、setcookie(),会触发 HTTP header 发送,后续中间件无法修改状态码
调试技巧:在每个中间件开头加 error_log("MW: xxx");,配合 tail -f /var/log/php/error.log 看执行顺序,比 var_dump 更可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











