PHP项目单例模式如何实现全局类

wen PHP项目 21

本文目录导读:

PHP项目单例模式如何实现全局类

  1. 经典单例模式(最常用)
  2. Trait方式(可复用)
  3. 延迟加载 + 线程安全
  4. 注册表模式(Registry Pattern)
  5. 实现全局静态类(final + private constructor)
  6. 推荐的最佳实践方案
  7. 使用建议

在PHP项目中实现单例模式的全局类,有多种方式,我为你整理了几种常见的实现方案:

经典单例模式(最常用)

<?php
class Database 
{
    private static ?Database $instance = null;
    // 私有构造函数,防止外部实例化
    private function __construct()
    {
        // 初始化数据库连接等
        echo "创建数据库连接";
    }
    // 私有克隆方法,防止克隆
    private function __clone() {}
    // 私有反序列化方法,防止反序列化
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize singleton");
    }
    // 获取单例实例
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    public function query(string $sql)
    {
        echo "执行查询: {$sql}";
    }
}
// 使用示例
$db = Database::getInstance();
$db->query("SELECT * FROM users");
$db2 = Database::getInstance();
var_dump($db === $db2); // true,同一个实例

Trait方式(可复用)

<?php
trait Singleton
{
    private static ?self $instance = null;
    private function __construct() {}
    private function __clone() {}
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize singleton");
    }
    public static function getInstance(): self
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}
// 使用Trait实现单例
class Logger
{
    use Singleton;
    public function log(string $message)
    {
        echo "[LOG]: {$message}\n";
    }
}
class Config
{
    use Singleton;
    private array $settings = [];
    public function set(string $key, $value): void
    {
        $this->settings[$key] = $value;
    }
    public function get(string $key)
    {
        return $this->settings[$key] ?? null;
    }
}
// 使用示例
$logger = Logger::getInstance();
$logger->log("系统启动");
$config = Config::getInstance();
$config->set("db_host", "localhost");
echo $config->get("db_host"); // localhost

延迟加载 + 线程安全

<?php
class CacheManager
{
    private static ?CacheManager $instance = null;
    private static array $locks = [];
    private function __construct()
    {
        // 初始化缓存
    }
    private function __clone() {}
    public static function getInstance(): self
    {
        // 双重检查锁定(Double-Checked Locking)
        if (self::$instance === null) {
            // 使用文件锁或数据库锁确保线程安全
            $lock = fopen(__FILE__ . '.lock', 'w');
            if (flock($lock, LOCK_EX)) {
                if (self::$instance === null) {
                    self::$instance = new self();
                }
                flock($lock, LOCK_UN);
            }
            fclose($lock);
        }
        return self::$instance;
    }
    public function get(string $key): mixed
    {
        return "缓存数据: {$key}";
    }
}

注册表模式(Registry Pattern)

<?php
class Registry
{
    private static array $instances = [];
    // 注册一个单例
    public static function set(string $key, object $instance): void
    {
        self::$instances[$key] = $instance;
    }
    // 获取一个单例
    public static function get(string $key): ?object
    {
        return self::$instances[$key] ?? null;
    }
    // 检查是否已注册
    public static function has(string $key): bool
    {
        return isset(self::$instances[$key]);
    }
    // 移除注册
    public static function remove(string $key): void
    {
        unset(self::$instances[$key]);
    }
}
// 使用注册表管理单例
class UserService
{
    public function getUsers(): array
    {
        return ['user1', 'user2'];
    }
}
// 注册单例
Registry::set('userService', new UserService());
// 获取单例
$userService = Registry::get('userService');
print_r($userService->getUsers());
// 判断是否已注册
if (Registry::has('userService')) {
    echo "UserService 已注册";
}

实现全局静态类(final + private constructor)

<?php
final class GlobalApp
{
    private static array $data = [];
    // 私有构造函数,防止实例化
    private function __construct() {}
    // 静态方法实现全局访问
    public static function set(string $key, $value): void
    {
        self::$data[$key] = $value;
    }
    public static function get(string $key): mixed
    {
        return self::$data[$key] ?? null;
    }
    public static function has(string $key): bool
    {
        return isset(self::$data[$key]);
    }
    public static function all(): array
    {
        return self::$data;
    }
}
// 使用示例(类似全局变量)
GlobalApp::set('app_name', 'My PHP App');
GlobalApp::set('version', '1.0.0');
echo GlobalApp::get('app_name'); // My PHP App
print_r(GlobalApp::all());

推荐的最佳实践方案

<?php
/**
 * 完整的单例基类
 */
abstract class SingletonBase
{
    protected static array $instances = [];
    // 禁止外部实例化
    protected function __construct() 
    {
        $this->init();
    }
    // 子类可以重写此方法进行初始化
    protected function init(): void {}
    // 禁止克隆
    protected function __clone() {}
    // 禁止反序列化
    public function __wakeup(): void
    {
        throw new \Exception("Cannot unserialize singleton");
    }
    // 获取实例
    final public static function getInstance(): static
    {
        $class = static::class;
        if (!isset(self::$instances[$class])) {
            self::$instances[$class] = new static();
        }
        return self::$instances[$class];
    }
    // 重置实例(用于测试)
    final public static function resetInstance(): void
    {
        $class = static::class;
        unset(self::$instances[$class]);
    }
}
// 实际使用
class AppConfig extends SingletonBase
{
    private array $config = [];
    protected function init(): void
    {
        // 加载配置文件
        $this->config = [
            'db_host' => 'localhost',
            'db_name' => 'myapp',
            'debug' => true
        ];
    }
    public function get(string $key, $default = null): mixed
    {
        return $this->config[$key] ?? $default;
    }
}
class AppLogger extends SingletonBase
{
    private string $logFile;
    protected function init(): void
    {
        $this->logFile = '/var/log/app.log';
    }
    public function log(string $message): void
    {
        $timestamp = date('Y-m-d H:i:s');
        file_put_contents(
            $this->logFile, 
            "[{$timestamp}] {$message}\n", 
            FILE_APPEND
        );
    }
}
// 使用示例
$config = AppConfig::getInstance();
echo $config->get('db_host'); // localhost
$logger = AppLogger::getInstance();
$logger->log("应用启动");
// 验证单例
$config2 = AppConfig::getInstance();
var_dump($config === $config2); // true

使用建议

  1. 选择合适的方案

    • 小型项目:使用经典单例模式
    • 大型项目:使用继承或Trait方式
    • 需要管理多个单例:使用注册表模式
  2. 注意线程安全

    • PHP-FPM模式下不需要担心
    • CLI模式下可能需要考虑
  3. 测试友好

    • 提供 resetInstance() 方法用于测试
    • 考虑使用依赖注入容器替代单例
  4. 避免滥用

    • 单例模式会增加耦合
    • 只在确实需要全局唯一实例时使用

选择哪种方式取决于你的具体需求和项目规模,我个人推荐使用Trait或继承方式,这样可以保持代码的整洁和可维护性。

抱歉,评论功能暂时关闭!