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

PHP 中间件不是要“学”的新语言特性,而是理解「函数链如何控制请求流向」——你不需要等框架教,现在就能手写一个可运行的调度器。
中间件本质就是 callable 链,不是类也不是接口
所谓中间件,就是一个接收 $request 和 $next、返回 $response 的函数。它不依赖 Laravel、Slim 或 PSR-15;没装 Composer 也能跑。
- 必须接收两个参数:
$request(数组或对象)和$next(下一个中间件的 callable) - 必须有
return语句,且返回值是$response;返回void或漏写return会导致整个链断裂 - 想终止流程?不调用
$next($request),直接return $response即可(比如鉴权失败时返回 401) - 想修改请求?改完
$request后再传给$next($request),而不是试图改$this->request(PHP 8.5+ 会报Creation of dynamic property)
手写 pipe() 调度器比引入框架更快上手
不用 Composer、不装 nyholm/psr7,几行 PHP 就能串起中间件链:
function pipe(array $middlewares, $request) {
$next = function ($req) use ($middlewares) {
if (empty($middlewares)) {
return ['status' => 'ok', 'body' => 'done'];
}
$mw = array_shift($middlewares);
return $mw($req, function ($r) use ($middlewares) {
return pipe($middlewares, $r);
});
};
return $next($request);
}
- 每个中间件只管自己逻辑,
$next是闭包递归生成的,天然支持洋葱模型 - 调试时在任意中间件里
var_dump(gettype($next)),确认它是Closure,不是null或字符串 - 别把
$next存成属性或全局变量——它只在当前调用栈有效,跨作用域会失效 - 如果中间件里用了
echo或exit,后续中间件和最终响应就没了;所有输出必须通过return返回结构化数据
PSR-15 中间件 ≠ 原生 PHP 支持,只是接口约定
看到 Class "Psr\Http\Server\MiddlewareInterface" not found 错误?这不是 PHP 版本问题,而是缺桥接包。
- 装 PSR-15 接口定义只需:
composer require psr/http-server-middleware(注意不是psr/http-message) -
process()方法签名必须严格为:process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface - 不能省略
$handler参数,也不能把它换成$next——PSR-15 不认$next这个名字 - 修改请求要用不可变方式:
$newRequest = $request->withHeader('X-Trace-ID', uniqid()),再传给$handler->handle($newRequest)
ThinkPHP/Laravel 等框架里中间件不生效,90% 是注册或执行时机问题
写了 handle() 方法却没触发?先看它有没有被真正加载进来。
- ThinkPHP:确认
app/middleware.php存在、返回数组、元素是完整命名空间(如\app\middleware\AuthCheck::class) - Laravel:依赖 session 或 auth 的中间件,不能放
$middleware全局数组里——得放进$middlewareGroups['web'],确保排在StartSession和Authenticate之后 - 所有框架都共享一个坑:在中间件里调了
$request->post()或$request->getContent(),结果后续控制器收不到 POST 数据——因为 request body 只能读一次 - 路由级中间件绑定要写对语法:
Route::get('/api')->middleware(\App\Middleware\Auth::class),别漏掉命名空间或写成字符串别名但没在配置里映射
最常被忽略的点是:中间件不是“插件”,它是请求流的必经关卡。一旦某个环节没 return、提前 echo、或错误地复用已读取的 request body,整个链就静默断开——浏览器白屏、日志无报错、调试器进不去下一层。动手前先确认 $next 是 callable,运行时盯住返回值类型,比背接口定义管用得多。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











