PHP职责链实现可插拔过滤系统的核心是接口契约与运行时组装:所有过滤器实现FilterInterface的handle()方法并接受$next,通过ChainBuilder动态添加、顺序执行,支持中断与条件插入,仅做预处理,返回array或抛异常。

PHP里用职责链实现可插拔过滤系统,核心是让每个过滤器只关心自己那部分逻辑,且能动态增删——不是靠继承硬编码,而是靠接口约定和运行时组装。
定义统一的FilterInterface和基础AbstractFilter
没有统一契约,链就断了。必须强制所有过滤器实现handle()方法,并接受下一个过滤器的引用。
-
FilterInterface只声明handle(array $data, callable $next): array,不暴露内部状态 - 实际开发中直接继承
AbstractFilter更省事,它默认把$next存为属性,并在子类没重写handle()时自动调用$next($data) - 别在
handle()里写return $next($data)这种固定跳转——要允许中断(比如验证失败直接返回,不往下传) - 如果某个过滤器需要提前终止链(如
AuthFilter鉴权失败),就别调用$next,直接return $data或抛异常
用ChainBuilder动态组装过滤器链
硬编码new AuthFilter(new SanitizeFilter(new LogFilter()))等于放弃“可插拔”——新增过滤器就得改构造函数。
- 用
ChainBuilder收集过滤器实例,最后调用build(): callable生成一个闭包:它接收$data,从第一个过滤器开始,逐个调用handle(),把前一个的返回值传给下一个 - 顺序很重要:
$builder->add(new AuthFilter())->add(new SanitizeFilter())意味着先鉴权再清洗,反了可能出安全问题 - 支持条件插入:
if ($env === 'prod') $builder->add(new RateLimitFilter()),这才是真正的运行时插拔 - 避免在
build()里做耗时操作(如读配置文件),否则每次请求都重复加载
在实际请求入口(如Router或MiddleWare)中调用链
职责链不是独立玩具,得嵌进真实流程里。常见错误是把它当成装饰器套在控制器外,结果控制器还得手动调用filter->handle()。
- 最佳位置是路由分发前:拿到原始
$_POST或json_decode(file_get_contents('php://input'))后,立刻喂给过滤链 - 返回值必须是
array(哪怕只加了一个'filtered_at' => time()),方便下游统一处理;如果某个过滤器抛异常(如InvalidInputException),应在最外层try/catch捕获并转成400响应 - 别把数据库查询、缓存读取塞进过滤器——职责链只做预处理(验证、清洗、日志、限流),业务逻辑留给控制器
- 性能敏感场景下,用
array_reduce()实现链式调用比递归更省内存,尤其过滤器多于5个时
真正难的不是写通职责链,而是定义清楚每个过滤器的边界:哪些该由SanitizeFilter做(比如trim()、htmlspecialchars()),哪些该交给后续的Validator(比如邮箱格式校验)。一旦职责模糊,链就变成黑盒,调试时连哪个环节改了数据都找不到。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











