必须私有化__construct、__clone和__wakeup,并声明private static $instance,否则单例模式失效:__construct公有则可new绕过;__clone公有则可clone复制实例;__wakeup未禁用则反序列化生成新实例;$instance非private static则可被外部篡改。

为什么必须私有化 __construct?
因为 PHP 的类默认允许外部用 new 创建任意多个实例。只要 __construct 是 public,哪怕你写了 getInstance,别人仍可绕过它直接 new MyClass() —— 单例就彻底失效了。
这不是“建议”,是硬性前提:没私有化 __construct,就不叫单例模式,只是个带静态方法的普通类。
__clone 私有化不是可选项,是防破环关键
对象一旦被创建出来,就可能被复制:$db2 = clone $db1;。如果 __clone 没设为 private,这行代码就能成功生成第二个独立实例,破坏单例语义。
常见错误现象:
立即学习“PHP免费学习笔记(深入)”;
- 两个看似相同的数据库连接对象,却各自维护独立的连接资源
- 修改其中一个的状态(比如事务开启标记),另一个完全不受影响
所以必须写:private function __clone() {},空实现即可,PHP 遇到 clone 会直接报致命错误。
静态属性 $instance 必须是 private static
这个变量是单例的“唯一容器”,如果它是 public 或 protected,外部代码就能直接赋值覆盖,比如:MyClass::$instance = new OtherClass();,瞬间绕过所有控制逻辑。
正确声明方式只有这一种:private static ?MyClass $instance = null;(PHP 8.0+ 推荐带类型声明)或 private static $instance = null;(兼容旧版)。
注意:不能省略 static,否则每次调用 getInstance 都会新建一个局部静态变量,无法跨调用保持状态。
别忽略 __wakeup:反序列化会悄悄绕过单例
当对象被 unserialize() 时,PHP 会跳过 __construct,直接重建对象——这就等于凭空多造了一个实例。
必须显式禁止:public function __wakeup() { throw new \Exception('Cannot unserialize singleton'); }
否则在以下场景会出问题:
- 从 Redis 缓存中取回序列化的单例对象
- 使用某些 ORM 或框架的自动反序列化机制
- 单元测试中 mock 后还原对象状态
这个方法常被遗漏,但它和 __construct、__clone 一样,是构成“三私”的刚性部分。



















