PHP项目单例与多例模式详解
单例模式 (Singleton Pattern)
核心概念
确保一个类只有一个实例,并提供全局访问点。

经典实现
class Database {
private static ?Database $instance = null;
private PDO $pdo;
// 私有化构造方法
private function __construct(array $config) {
$this->pdo = new PDO(
"mysql:host={$config['host']};dbname={$config['dbname']}",
$config['user'],
$config['pass']
);
}
// 禁止克隆
private function __clone() {}
// 禁止反序列化
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
// 获取实例
public static function getInstance(array $config = []): self {
if (self::$instance === null) {
self::$instance = new self($config);
}
return self::$instance;
}
public function query(string $sql): array {
return $this->pdo->query($sql)->fetchAll();
}
}
// 使用
$db = Database::getInstance(['host' => 'localhost', ...]);
$db2 = Database::getInstance(); // 返回同一实例
实际应用场景
// 1. 配置管理
class Config {
private static ?Config $instance = null;
private array $settings = [];
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function load(string $file): void {
$this->settings = parse_ini_file($file);
}
public function get(string $key, $default = null) {
return $this->settings[$key] ?? $default;
}
}
// 2. 日志记录
class Logger {
private static array $instances = [];
private string $channel;
private function __construct(string $channel) {
$this->channel = $channel;
}
public static function getInstance(string $channel): self {
if (!isset(self::$instances[$channel])) {
self::$instances[$channel] = new self($channel);
}
return self::$instances[$channel];
}
public function log(string $message): void {
echo "[{$this->channel}] $message\n";
}
}
多例模式 (Multiton Pattern)
核心概念
单例模式的扩展,维护多个命名实例的集合。
经典实现
class DatabaseConnection {
private static array $instances = [];
private string $name;
private PDO $pdo;
private function __construct(string $name, array $config) {
$this->name = $name;
$this->pdo = new PDO(
"mysql:host={$config['host']};dbname={$config['dbname']}",
$config['user'],
$config['pass']
);
}
private function __clone() {}
public static function getInstance(string $name, array $config = []): self {
if (!isset(self::$instances[$name])) {
self::$instances[$name] = new self($name, $config);
}
return self::$instances[$name];
}
public static function exists(string $name): bool {
return isset(self::$instances[$name]);
}
public static function remove(string $name): void {
unset(self::$instances[$name]);
}
public function getPdo(): PDO {
return $this->pdo;
}
}
// 使用
$primary = DatabaseConnection::getInstance('primary', [
'host' => '192.168.1.1',
'dbname' => 'main_db'
]);
$replica = DatabaseConnection::getInstance('replica', [
'host' => '192.168.1.2',
'dbname' => 'read_db'
]);
高级多例模式实现
abstract class Multiton {
private static array $instances = [];
protected function __construct() {}
final public static function getInstance(string $key = 'default'): static {
$class = get_called_class();
if (!isset(self::$instances[$class][$key])) {
self::$instances[$class][$key] = new static();
}
return self::$instances[$class][$key];
}
final public static function reset(): void {
$class = get_called_class();
unset(self::$instances[$class]);
}
final private function __clone() {}
}
// 具体实现
class DatabaseHandler extends Multiton {
public function connect(array $config): void {
// 连接逻辑
}
}
class CacheHandler extends Multiton {
public function set(string $key, $value): void {
// 缓存逻辑
}
}
// 使用
$db = DatabaseHandler::getInstance('primary');
$cache = CacheHandler::getInstance('redis');
实际项目中的最佳实践
线程安全考虑
class ThreadSafeSingleton {
private static array $instances = [];
private static array $locks = [];
public static function getInstance(string $key = 'default'): self {
// 双重检查锁定
if (!isset(self::$instances[$key])) {
self::$locks[$key] = self::$locks[$key] ?? new \stdClass();
synchronized(self::$locks[$key]) {
if (!isset(self::$instances[$key])) {
self::$instances[$key] = new self();
}
}
}
return self::$instances[$key];
}
}
延迟初始化优化
class LazySingleton {
private static ?self $instance = null;
public static function getInstance(): self {
return self::$instance ??= new self();
}
// 使用生成器实现懒加载
public static function getLazyInstance(): \Generator {
yield self::getInstance();
}
}
依赖注入容器中的使用
class ServiceContainer {
private static array $services = [];
private static array $factories = [];
public static function set(string $name, callable $factory): void {
self::$factories[$name] = $factory;
}
public static function get(string $name): object {
if (!isset(self::$services[$name])) {
self::$services[$name] = self::$factories[$name]();
}
return self::$services[$name];
}
public static function singleton(string $name, callable $factory): void {
self::set($name, function() use ($factory) {
static $instance = null;
return $instance ??= $factory();
});
}
}
// 注册服务
ServiceContainer::singleton('db', function() {
return new DatabaseConnection(['host' => 'localhost']);
});
ServiceContainer::singleton('cache', function() {
return new CacheManager(['driver' => 'redis']);
});
性能对比与选择建议
| 特性 | 单例模式 | 多例模式 |
|---|---|---|
| 实例数量 | 1个 | 有限个(lt;10) |
| 资源消耗 | 最小 | 较小 |
| 使用场景 | 全局唯一资源 | 多种配置的资源 |
| 测试难度 | 中等 | 更容易 |
| 扩展性 | 差 | 较好 |
选择建议
// 单例模式适用场景 - 配置管理 - 日志记录器 - 数据库连接池 - 缓存管理器 // 多例模式适用场景 - 多数据库连接 - 多文件系统连接 - 多API服务连接 - 多缓存层管理 // 避免使用的情况 - 需要频繁创建/销毁的对象 - 状态可变的对象 - 需要高度测试覆盖的代码
常见的陷阱与解决方案
循环依赖
// 错误示例
class ServiceA {
public function __construct() {
ServiceB::getInstance();
}
}
// 解决方案:使用注入容器
class ServiceProvider {
public function boot(): void {
app()->singleton('serviceA', function() {
return new ServiceA(app()->make('serviceB'));
});
app()->singleton('serviceB', function() {
return new ServiceB();
});
}
}
资源泄漏
class ManagedSingleton {
private static ?self $instance = null;
private bool $initialized = false;
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function cleanup(): void {
if ($this->initialized) {
// 清理资源
$this->initialized = false;
}
}
public function __destruct() {
$this->cleanup();
}
}
现代PHP框架中的实现
Laravel中的门面模式
use Illuminate\Support\Facades\Cache;
class UserService {
public function getUser(int $id): ?User {
return Cache::remember("user_{$id}", 3600, function() use ($id) {
return User::find($id);
});
}
}
Symfony中的服务容器
# services.yaml
services:
app.database_connection:
class: App\DatabaseConnection
arguments:
- '%database_host%'
- '%database_name%'
# 默认是shared: true(单例)
shared: true
app.mailer:
class: App\Mailer
shared: false # 每次获取新实例
- 单例模式适用于需要全局唯一实例的场景,但要注意线程安全和资源管理
- 多例模式适用于需要管理有限数量的同名但不同配置的实例
- 现代框架已经提供了强大的依赖注入容器,建议优先使用容器管理单例
- 注意避免在构造函数中进行耗时操作,使用延迟初始化优化性能
- 在测试环境中,考虑提供重置静态属性的方法以便于隔离测试