PHP原生无Observer接口,SPL的SplSubject/SplObserver仅提供契约且已废弃;推荐用Symfony EventDispatcher,支持事件名、优先级、停止传播等;手写需注意监听器存储、异常捕获、内存泄漏及事件命名规范。

PHP 原生不提供 Observer 接口或 EventDispatcher 类,但用 SPL 的 SplSubject 和 SplObserver 可快速搭出符合规范的观察者模式;不过实际项目中更推荐用事件分发器(如 Symfony EventDispatcher)——因为 SPL 实现缺乏事件名过滤、优先级、停止传播等关键能力。
为什么不用 SplSubject/SplObserver 直接开干?
它们只是接口契约,不带实现逻辑:SplSubject::notify() 不自动遍历观察者,SplObserver::update() 参数固定为 SplSubject 实例,无法传业务数据。你得自己补全:
- 手动维护观察者数组、注册/移除逻辑
- 每次
notify()都要循环调用update($subject),没法传额外参数(比如事件名、payload) - 没有“事件名”概念,所有观察者对同一个 subject 的任何变更都会被触发,无法做细粒度监听
- PHP 8.2+ 已将
SplObserver标记为废弃(@deprecated),官方不鼓励继续使用
用 symfony/event-dispatcher 实现真实可用的事件监听
这是当前最稳妥的方案,支持事件名、事件对象、监听器优先级、事件停止传播等。安装后只需三步:
- 定义事件类(继承
Symfony\Contracts\EventDispatcher\Event或直接用空对象) - 在业务代码中获取
EventDispatcherInterface实例,调用dispatch($event, $eventName) - 通过配置或
addListener()注册监听器,监听指定$eventName
示例片段:
// 发布事件
$event = new OrderPaidEvent($order);
$dispatcher->dispatch($event, 'order.paid');
// 监听器(可设优先级)
$dispatcher->addListener('order.paid', [$this, 'onOrderPaid'], 10);
手写轻量事件分发器要注意哪些坑?
如果不想引入组件,自己写一个最小可用版,必须处理好这几个点:
- 监听器存储结构用
array $listeners['event.name'] = [[$obj, 'method'], ...],别用简单一维数组,否则无法按事件名筛选 -
dispatch()方法必须支持传递任意参数(建议封装成Event对象,避免参数列表膨胀) - 监听器执行时需捕获异常,否则一个监听器崩了会导致后续全部跳过(除非你明确要“中断式”语义)
- 别忘了支持
removeListener($event, $callback)—— 尤其在长生命周期对象(如 CLI 命令)中,否则会内存泄漏
事件名命名和作用域容易被忽略
事件名不是随便起的字符串:user.registered 比 user_register 更利于层级管理;多模块共用时,加前缀防冲突(如 payment:refund.completed)。更重要的是——事件应在明确边界内分发:Repository 层不该 dispatch 表单验证失败事件,那是 Form 或 Request 层的责任。否则监听器会跨层耦合,调试时根本理不清谁在什么时候触发了什么。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











