PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_TIMEOUT => 5, ]); self::$pdo->exec("SET time_zone = '+08:00'"); } return self::$pdo; } /** 执行 prepared statement(唯一写库/查询路径)。 */ public static function run(string $sql, array $params = []): \PDOStatement { $stmt = self::pdo()->prepare($sql); $stmt->execute($params); return $stmt; } public static function fetchAll(string $sql, array $params = []): array { return self::run($sql, $params)->fetchAll(); } public static function fetchOne(string $sql, array $params = []): ?array { $row = self::run($sql, $params)->fetch(); return $row === false ? null : $row; } public static function fetchValue(string $sql, array $params = []): mixed { $value = self::run($sql, $params)->fetchColumn(); return $value === false ? null : $value; } /** 插入一行(表名白名单校验),返回自增 ID。 */ public static function insert(string $table, array $data): int { if (!preg_match('/^[a-z][a-z0-9_]*$/', $table)) { throw new \InvalidArgumentException("非法表名: {$table}"); } $q = chr(96); // 反引号字符,MySQL 标识符引用 $columns = array_keys($data); $quoted = array_map(static fn(string $c) => $q . $c . $q, $columns); $sql = sprintf( 'INSERT INTO %s (%s) VALUES (%s)', $table, implode(', ', $quoted), implode(', ', array_map(static fn(string $c) => ':' . $c, $columns)) ); self::run($sql, $data); return (int) self::pdo()->lastInsertId(); } }