laravel 5.5 中间件管道基于 array_reduce + 闭包链实现函数式组合,所有中间件平等参与双向流转,执行顺序由反转数组后 reduce 决定,参数通过 pipeline 预绑定传递,terminate() 独立于 pipeline 用于异步资源清理。

因为 Laravel 5.5 的中间件管道本质是 array_reduce + 闭包链的函数式组合,不是硬编码的执行顺序,所有中间件都平等参与“请求→响应”双向流转,参数、顺序、跳过逻辑全由运行时决定。
中间件执行顺序由 array_reduce(array_reverse($pipes), $carry, $destination) 决定
Laravel 并不靠 for 循环或手动调用数组元素来执行中间件。它把中间件列表 $pipes 反转后喂给 array_reduce,再用 $carry(一个返回新闭包的函数)逐层包裹 $destination(最终控制器逻辑)。结果是一个嵌套闭包链,形如:
function ($request) {
// 最外层:第一个中间件
return $middleware1->handle($request, function ($request) {
// 第二层:第二个中间件
return $middleware2->handle($request, function ($request) {
// ... 依此类推
return $controller($request);
});
});
}
这意味着:
- 添加/删除中间件只需改数组内容,无需动核心逻辑
- 中间件本身完全无状态,不依赖彼此上下文
- 反转数组是为了让「最先注册的中间件」在最外层,从而最早拦截请求、最晚处理响应
handle($request, Closure $next, ...$parameters) 的参数穿透机制
路由中写 ->middleware('role:admin,editor'),框架会自动把 admin 和 editor 作为第三、第四个参数传入 handle 方法。这背后是 Illuminate\Pipeline\Pipeline 在 prepareDestination 阶段做的参数绑定,而非 PHP 自动解析。
容易踩的坑:
- 参数名必须严格按顺序声明,不能跳过中间某个参数(比如只写
handle($req, $next, $role)却传了两个值,第二个会被丢弃) - 参数值不会自动类型转换,
'1'是字符串,不是整型1 - 如果中间件类没定义
$parameters对应的参数,PHP 会抛出ArgumentCountError
为什么 terminate() 不走 pipeline,却能保证执行?
terminate($request, $response) 是独立于 pipeline 的钩子,在响应已发送给客户端之后才触发。它不参与 array_reduce 链,而是由 Kernel::terminate() 统一收集并调用所有实现了该方法的中间件。
这个设计的关键在于解耦:
-
handle()负责「请求流控制」,必须同步、阻塞、可中断 -
terminate()负责「资源清理」,允许异步、非关键、可失败(比如 session 写入失败不影响响应) - 两者生命周期不同,混在一起反而增加复杂度和错误风险
真正灵活的地方不在语法糖,而在整个 pipeline 是纯函数式构造:没有状态共享、没有隐式依赖、每个中间件只关心自己那层的输入输出。一旦你开始手动在中间件里改全局变量、缓存静态属性,或者绕过 $next($request) 直接 return,这种灵活性就立刻坍缩成脆弱的顺序耦合。











