PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci", ]; try { self::$instance = new PDO($dsn, DB_USER, DB_PASS, $options); } catch (PDOException $e) { // Never expose DB details in production error_log("DB Connection failed: " . $e->getMessage()); die(json_encode(['error' => 'Database connection failed. Please try again later.'])); } } return self::$instance; } // Quick helper: run a query with params, return PDOStatement public static function query(string $sql, array $params = []): PDOStatement { $stmt = self::get()->prepare($sql); $stmt->execute($params); return $stmt; } // Fetch single row public static function row(string $sql, array $params = []): ?array { $row = self::query($sql, $params)->fetch(); return $row ?: null; } // Fetch all rows public static function all(string $sql, array $params = []): array { return self::query($sql, $params)->fetchAll(); } // Insert and return last insert id public static function insert(string $sql, array $params = []): int { self::query($sql, $params); return (int) self::get()->lastInsertId(); } // Get app setting from DB public static function getSetting(string $key, string $default = ''): string { $row = self::row("SELECT `value` FROM settings WHERE `key` = ?", [$key]); return $row ? (string)($row['value'] ?? $default) : $default; } }