確保一個類在全局範圍內**僅有一個實例**,並提供一個全局訪問點。核心職責:控制實例化過程,防止多次 new 消耗資源或狀態不一致。
private function __construct() 防止外部 new。private static $_instance = null; 保存唯一實例。public static function getInstance() 延遲實例化並返回引用。輔助:防止克隆:private function __clone() 拋出錯誤。
<?php
declare(strict_types=1);
class Database {
private static ?self $_instance = null;
private \PDO $pdo;
// 1. 私有構造:完成資源初始化
private function __construct() {
$dsn = 'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4';
$this->pdo = new \PDO($dsn, 'user', 'pass', [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION,
\PDO::ATTR_DEFAULT_FETCH_MODE => \PDO::FETCH_ASSOC,
]);
echo "Database connected.\n";
}
// 2. 防止克隆
private function __clone() {
throw new \LogicException('Cloning singleton is forbidden.');
}
// 3. 防止反序列化(可選)
public function __wakeup() {
throw new \LogicException('Unserializing singleton is forbidden.');
}
// 4. 獲取唯一實例
public static function getInstance(): self {
if (self::$_instance === null) {
self::$_instance = new self();
}
return self::$_instance;
}
// 業務方法
public function query(string $sql, array $params = []): array {
$stmt = $this->pdo->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll();
}
}
// 使用方式
$db = Database::getInstance();
$users = $db->query('SELECT * FROM users WHERE status = ?', [1]);
// 嘗試克隆將拋出異常
// $db2 = clone $db; // LogicException
| 場景 | 收益 |
|---|---|
| 資料庫連接 | 避免重複建立 TCP 連接、認證握手,節省資源與延遲 |
| 配置管理 | 全局唯一配置中心,運行時熱更新、多模組共享 |
| 日誌記錄器 | 單一寫入通道,避免文件句柄衝突、格式統一 |
| 快取客戶端 | Redis/Memcached 連接池復用 |
resetInstance() 僅供測試環境調用,或使用 runkit/uopz 重置。singleton,實例由容器管理生命週期,支援自動注入、介面綁定、標籤解析。services.yaml 配置 shared: true(預設)。單例模式是「雙刃劍」:正確使用能優化資源佔用、簡化全局訪問;濫用則導致隱式依賴、測試困難、擴展受限。**原則:資源型單例,業務型依賴注入。**