如何使用单例模式正确编写PHP数据库类?附实现代码求校验建议
现有实现问题梳理
单例逻辑缺陷
- 静态属性
$instance存储的是PDO对象而非Database类自身的实例,属于错位的伪单例实现,后续给Database类扩展自定义属性/方法时会出现异常 - 缺少对
__wakeup魔术方法的私有化限制,反序列化操作仍可生成新的Database实例,单例防护不完整 getInstance方法中直接调用new Database()却不赋值给静态变量,依赖构造方法内部给$instance赋值的逻辑不符合单例的常规实现规范,可读性和可维护性极差
代码语法&逻辑bug
read方法的花括号配对错误,if($stm)判断块多了一个闭合括号,会导致$result变量未定义的报错read是实例方法,测试代码直接用静态调用Database::read(),开启PHP严格模式后会抛出E_STRICT级错误- 构造方法中直接执行
die()终止程序,无法让上层逻辑捕获异常做兜底处理,线上环境排查问题难度极高
优化后实现方案
<?php class Database { // 存储Database类自身的单例 protected static ?Database $instance = null; // 存储PDO连接实例 protected PDO $pdo; private static string $dsn = DB_TYPE.":host=".DB_HOST.";dbname=".DB_NAME; private static string $username = DB_USER; private static string $password = DB_PASS; private function __construct() { try { $this->pdo = new PDO(self::$dsn, self::$username, self::$password); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); } catch (PDOException $e) { throw new Exception("数据库连接失败:".$e->getMessage()); } } // 禁止克隆、反序列化生成新实例 private function __clone() {} private function __wakeup() {} public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function read(string $query, array $data = []): array|bool { $stm = $this->pdo->prepare($query); $result = false; if ($stm) { $check = $stm->execute($data); if ($check) { $result = $stm->fetchAll(PDO::FETCH_OBJ); } } return is_array($result) && count($result) > 0 ? $result : false; } } // 测试调用 echo '<pre>'; print_r(Database::getInstance()->read("SELECT * FROM `table`"));
额外建议
PHP场景下单例仅在单次请求生命周期内有效,无法实现跨请求的连接复用,如果后续项目复杂度提升,更推荐用依赖注入容器管理数据库连接实例,比硬编码的单例更适合做单元测试和功能扩展。
内容的提问来源于stack exchange,提问作者user16361980
相关产品推荐
相关产品推荐

