PHP 单例模式

概述

单例模式是 PHP 中最常用的创建型设计模式之一,核心目标是保证一个类在整个应用生命周期内仅有一个实例,并提供全局统一的访问入口。其核心价值在于:

  • 减少资源消耗:避免重复创建数据库连接、缓存实例、日志工具等资源密集型对象;

  • 保证数据一致性:全局唯一实例可确保共享状态(如配置信息)的一致性;

  • 简化全局访问:无需频繁通过参数传递对象,通过固定方法即可获取实例。

代码

<?php
declare(strict_types = 1);

/**
 * 单例模式:保证类仅有一个实例,并提供全局访问点
 * Class SingleObject
 * @package Extend
 */
class SingleObject
{
    // 1. 私有静态属性:存储唯一实例(添加 readonly 限制,避免外部篡改)
    private static readonly ?self $instance = null;

    // 2. 私有构造方法:禁止类外通过 new 实例化
    private function __construct()
    {
        // 可选:添加初始化逻辑(如连接数据库、加载配置)
        // $this->init();
    }

    // 3. 私有克隆方法:禁止类外通过 clone 复制实例
    private function __clone() {}

    // 4. 禁止序列化:避免通过反序列化创建新实例
    private function __wakeup() {}

    // 5. 公有静态方法:获取唯一实例(添加 final 防止子类重写,确保单例逻辑不被破坏)
    public static final function getInstance(): self
    {
        // 双重检查锁定(DCL):解决多线程/并发场景下的实例重复创建问题
        if (is_null(self::$instance)) {
            // 加锁:阻止并发请求同时进入实例化逻辑
            synchronized(function () {
                if (is_null(self::$instance)) {
                    self::$instance = new self();
                }
            });
        }
        return self::$instance;
    }

    // 示例:单例类的业务方法
    public function doSomething(): string
    {
        return "单例对象执行业务逻辑";
    }
}

// 测试代码
$obj1 = SingleObject::getInstance();
$obj2 = SingleObject::getInstance();

// 验证是否为同一实例(输出 bool(true))
var_dump($obj1 === $obj2);
// 调用业务方法(输出 string(24) "单例对象执行业务逻辑")
echo $obj1->doSomething();

说明

  1. 并发安全

    • 上述代码中 synchronized() 是 PHP 8.1+ 提供的内置函数,用于实现互斥锁;

    • 若使用 PHP 8.1 以下版本,可通过 Mutex 扩展或自定义锁机制(如文件锁、Redis 锁)替代。

  2. 反射攻击防护

    • 原文代码未防护反射攻击(通过反射调用私有构造方法),若需严格防护,可在构造方法中添加判断:
      private function __construct()
      {
       if (!is_null(self::$instance)) {
           throw new \RuntimeException("单例类不允许重复实例化");
       }
      }
  3. 适用场景

    • 该单例模式适用于数据库连接、缓存服务、日志工具、配置管理等需要全局唯一实例的场景;
    • 避免在简单脚本或无共享状态的场景中过度使用单例,否则会增加代码耦合度。