不能用了——php 8.5 已彻底移除 splsubject 和 splobserver 类及接口,运行时直接报错;替代方案是手写两个接口加 weakmap 实现的 basesubject 基类,确保解耦、类型安全与内存安全。

PHP 8.5 里 SplSubject 和 SplObserver 还能用吗?
不能直接用了——PHP 8.5 已彻底移除 SplSubject、SplObserver 及其相关接口。这不是弃用警告,是真实删除。你如果在升级后看到 Fatal error: Uncaught Error: Class "SplSubject" not found,就是这个原因。
PHP 官方从 8.3 开始标记为废弃,8.4 移除类定义,8.5 仅保留空桩(部分构建可能连桩都没了),实际运行时必然报错。
- 别试图用
class_alias()或手动定义接口来“恢复”,它们依赖的底层 C 层钩子已剥离 -
ext/spl扩展本身还在,但这两个类不再注册到全局命名空间 - Composer 包如
phpspec/prophecy等若硬依赖它们,在 PHP 8.5+ 下会直接 fail install
替代方案:手写观察者接口最稳
不用框架、不引入新包,三分钟就能写出兼容 PHP 8.5+ 的轻量观察者。核心就两个接口 + 一个可复用的被观察者基类。
重点不是“多像 SPL”,而是保证解耦、类型安全、支持属性监听(比如只响应 status 变更)。
立即学习“PHP免费学习笔记(深入)”;
- 定义
ObserverInterface,带update(SubjectInterface $subject, array $payload = [])方法 - 定义
SubjectInterface,含attach()、detach()、notify() - 实现
BaseSubject时用WeakMap存 observer(避免循环引用内存泄漏) - 不要用
array存 observer 列表——PHP 8.5 中foreach遍历时修改数组仍可能跳过项,WeakMap更可靠
interface SubjectInterface
{
public function attach(ObserverInterface $observer): void;
public function detach(ObserverInterface $observer): void;
public function notify(array $payload = []): void;
}
class BaseSubject implements SubjectInterface
{
private WeakMap $observers;
public function __construct() {
$this->observers = new WeakMap();
}
public function attach(ObserverInterface $observer): void {
$this->observers[$observer] = true;
}
public function notify(array $payload = []): void {
foreach ($this->observers as $observer => $_) {
$observer->update($this, $payload);
}
}
}
为什么不用 Laravel / Symfony 的事件系统?
如果你项目已用 Laravel,event() 函数或 dispatch() 是更自然的选择;Symfony 用 EventDispatcherInterface。但它们和原始观察者模式不是一回事:
- Laravel 事件默认同步,但支持队列化——而观察者模式隐含“同步响应状态变更”,加队列反而破坏语义
- Symfony 的事件名是字符串(如
"user.registered"),类型不可查;手写接口可让 IDE 跳转、静态分析捕获拼写错误 - 第三方事件系统通常要求服务容器绑定,而小工具类(比如配置管理器、日志上下文)没必要拉起整个 DI
- 性能上,纯接口调用比 EventDispatcher 多一层反射+事件名解析,微乎其微,但可控
容易漏掉的细节:通知时机与 payload 设计
很多人写完 notify() 就以为完了,结果 observer 收到的是旧状态或空数据。关键不在“发”,而在“发什么”和“什么时候发”。
- 别在 setter 末尾无脑
$this->notify()——如果多个属性连续改(如$user->setName()->setEmail()->save()),会触发三次通知,但业务可能只关心“保存完成”那一刻 - 推荐把
notify()放在明确的生命周期点,比如save()、publish()、transitionTo() -
$payload至少包含'event'键(如'user.updated')和'changes'(变更字段数组),方便 observer 做条件过滤 - 避免传大对象(如整个
$this)进$payload——observer 不一定需要全部数据,且可能引发意外修改
观察者模式在 PHP 8.5+ 不是“配不配”的问题,是“要不要自己控住边界”的问题。SPL 的消失反而是提醒:轻量契约比内置黑盒更值得信任。











