單例模式限制類別只能實例化一次,並提供全域存取點。常用於資料庫連線、配置管理、日誌記錄器等資源密集型物件。
<?php
class DatabaseConnection {
private static ?self $instance = null;
private PDO $pdo;
private function __construct() {
$dsn = 'mysql:host=localhost;dbname=app;charset=utf8mb4';
$this->pdo = new PDO($dsn, 'user', 'pass', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]);
}
public static function getInstance(): self {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 禁止複製與反序列化
private function __clone() {}
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
public function getPdo(): PDO {
return $this->pdo;
}
}
// 使用
$pdo = DatabaseConnection::getInstance()->getPdo();
$stmt = $pdo->query('SELECT 1');
?>
new。clone 或 unserialize 產生第二個實例。?self, PDO 提升 IDE 支援與靜態分析。| 優點 | 缺點 |
|---|---|
| 全域唯一存取,避免重複連線 | 全域狀態難測試,單元測試需模擬或重置 |
| 延遲初始化,用到才建立 | 隱性依賴,違反依賴倒置原則 |
| 記憶體節省 | 多執行緒環境需鎖 (PHP-FPM 無此問題) |
<?php
// 使用 PHP-DI / Symfony DI / Laravel Container
$container->set(PDO::class, function() {
return new PDO($dsn, $user, $pass, $options);
});
// 在需要處注入
class UserRepository {
public function __construct(private PDO $pdo) {}
}
?>
DI 容器管理生命週期 (singleton scope),保持程式碼可測試、低耦合。單例模式仍適用於簡單腳本、舊專案或無 DI 容器環境。