= 80500 ? \Pdo\Mysql::ATTR_INIT_COMMAND : \PDO::MYSQL_ATTR_INIT_COMMAND; try { $this->pdo = new PDO($dsn, $config['username'], $config['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, $initCommand => 'SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci', ]); $tz = $config['timezone'] ?? 'UTC'; $offset = (new \DateTimeZone($tz))->getOffset(new \DateTimeImmutable('now', new \DateTimeZone('UTC'))); $sign = $offset >= 0 ? '+' : '-'; $offset = abs($offset); $hours = str_pad((string) intdiv($offset, 3600), 2, '0', STR_PAD_LEFT); $minutes = str_pad((string) (($offset % 3600) / 60), 2, '0', STR_PAD_LEFT); $tzOffset = sprintf('%s%s:%s', $sign, $hours, $minutes); $this->pdo->exec("SET time_zone = '{$tzOffset}'"); } catch (PDOException $e) { throw new \RuntimeException('Database connection failed: ' . $e->getMessage()); } } public static function getInstance(array $config = []): self { if (self::$instance === null) { if (empty($config)) { throw new \RuntimeException('Database configuration is required for first initialization'); } self::$instance = new self($config); } return self::$instance; } public static function resetInstance(): void { self::$instance = null; } public function getPdo(): PDO { return $this->pdo; } public function query(string $sql, array $params = []): \PDOStatement { $stmt = $this->pdo->prepare($sql); $stmt->execute($params); return $stmt; } public function fetch(string $sql, array $params = []): ?array { $result = $this->query($sql, $params)->fetch(); return $result ?: null; } public function fetchAll(string $sql, array $params = []): array { return $this->query($sql, $params)->fetchAll(); } public function fetchCol(string $sql, array $params = []): array { return $this->query($sql, $params)->fetchAll(\PDO::FETCH_COLUMN); } public function insert(string $table, array $data): int { $columns = implode(', ', array_keys($data)); $placeholders = implode(', ', array_fill(0, count($data), '?')); $sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})"; $this->query($sql, array_values($data)); return (int) $this->pdo->lastInsertId(); } public function update(string $table, array $data, string $where, array $whereParams = []): int { $sets = implode(', ', array_map(fn($col) => "{$col} = ?", array_keys($data))); $sql = "UPDATE {$table} SET {$sets} WHERE {$where}"; $stmt = $this->query($sql, array_merge(array_values($data), $whereParams)); return $stmt->rowCount(); } public function delete(string $table, string $where, array $params = []): int { $sql = "DELETE FROM {$table} WHERE {$where}"; $stmt = $this->query($sql, $params); return $stmt->rowCount(); } public function beginTransaction(): bool { return $this->pdo->beginTransaction(); } public function commit(): bool { return $this->pdo->commit(); } public function rollback(): bool { return $this->pdo->rollBack(); } }