
PHP里怎么用类和接口搭出责任链
责任链不是靠语言特性,而是靠对象之间“传话”实现的:每个处理器决定自己干不干、干完要不要往下传。PHP没内置责任链语法糖,得靠接口约定 + 类继承来组织。
核心就两条:handle() 方法必须返回 bool 或 null 表示是否终止;每个处理器持有一个 $next 属性指向下一个处理器(可以是 null)。
- 别让
handle()直接 return 值——它只管流程控制;业务结果该放哪放哪,比如塞进请求对象或返回值里 - 链的组装必须手动 new 并 setNext,没有自动发现机制,别指望 Composer 自动连起来
- 如果某个处理器抛异常但没 catch,整个链就断了,后续节点完全不会执行
为什么不能用 if-else 替代责任链
表面上看,一堆 if-else 也能顺序判断、提前退出,但责任链解决的是「规则动态组合」和「关注点分离」问题。
比如权限校验场景:AuthMiddleware、RateLimitMiddleware、LogMiddleware 各自只关心自己那块逻辑,谁加谁删不影响别人。而 if-else 写一块,改一个条件就得通读整段逻辑。
立即学习“PHP免费学习笔记(深入)”;
- if-else 难测试:你没法单独测“只有 RateLimit 生效时的行为”
- if-else 难复用:想在 CLI 命令里复用部分中间件?得把逻辑抠出来重写
- if-else 难调试:出问题时不知道卡在哪一环,而责任链每个节点可独立打日志
PHP 8.1+ 属性提升会让链式构造更干净吗
会,但仅限于减少样板代码,不改变责任链本质。PHP 8.1 的构造器属性提升能帮你省掉重复的属性声明和赋值,但 $next 的连接逻辑还得手写。
class AuthHandler {
public function __construct(
private ?self $next = null,
private string $role = 'user'
) {}
public function handle($request): ?bool {
if ($request['role'] !== $this->role) {
return false;
}
return $this->next?->handle($request) ?? true;
}
}
- 注意
$next类型必须是当前类或其父类,不能是接口——PHP 不支持接口类型用于属性提升(会报错Typed property must not be a generic type) - 使用
?? true是常见惯用法,表示“走到链尾默认成功”,但你要的是失败兜底,就得改成?? false - PHP 7.4 的
??和 8.0 的空合并运算符?->在这里很实用,但别滥用:如果$next可能是其他类型(比如闭包),就会出错
链太长或循环引用时 PHP 会爆什么错
最典型的是 Fatal error: Allowed memory size exhausted,本质是递归调用栈溢出,不是内存泄漏。
比如 A → B → C → A 这种闭环,或者某节点忘了设 $next,却在 handle() 里无条件调 $this->next->handle(),就会无限递归。
- 调试时加一句
debug_print_backtrace(DEBUG_BACKTRACE_LIMIT=5)能快速定位哪一层开始套娃 - 生产环境别依赖 try-catch 捕获
Fatal error——它无法被常规异常捕获机制拦截 - 链长度超过 100 层基本就该警觉了:是不是误把循环数据当链路配置传进来了?比如用数组 key 当 handler 名,结果 key 重复导致覆盖











