本文目录导读:

- 经典单例模式(最常用)
- Trait方式(可复用)
- 延迟加载 + 线程安全
- 注册表模式(Registry Pattern)
- 实现全局静态类(final + private constructor)
- 推荐的最佳实践方案
- 使用建议
在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
使用建议
-
选择合适的方案:
- 小型项目:使用经典单例模式
- 大型项目:使用继承或Trait方式
- 需要管理多个单例:使用注册表模式
-
注意线程安全:
- PHP-FPM模式下不需要担心
- CLI模式下可能需要考虑
-
测试友好:
- 提供
resetInstance()方法用于测试 - 考虑使用依赖注入容器替代单例
- 提供
-
避免滥用:
- 单例模式会增加耦合
- 只在确实需要全局唯一实例时使用
选择哪种方式取决于你的具体需求和项目规模,我个人推荐使用Trait或继承方式,这样可以保持代码的整洁和可维护性。