ping()) { self::$connection = self::createConnection(); } return self::$connection; } /** * Create a new database connection * * @return mysqli Database connection * @throws Exception If connection fails */ private static function createConnection(): mysqli { // Ensure config is loaded if (!isset($GLOBALS['config'])) { require_once dirname(__DIR__) . '/config/config.php'; } $conn = new mysqli( $GLOBALS['config']['DB_HOST'], $GLOBALS['config']['DB_USER'], $GLOBALS['config']['DB_PASS'], $GLOBALS['config']['DB_NAME'] ); if ($conn->connect_error) { throw new Exception("Database connection failed: " . $conn->connect_error); } // Set charset to utf8mb4 for proper Unicode support $conn->set_charset('utf8mb4'); return $conn; } /** * Close the database connection */ public static function close(): void { if (self::$connection !== null) { self::$connection->close(); self::$connection = null; } } /** * Begin a transaction * * @return bool Success */ public static function beginTransaction(): bool { return self::getConnection()->begin_transaction(); } /** * Commit a transaction * * @return bool Success */ public static function commit(): bool { return self::getConnection()->commit(); } /** * Rollback a transaction * * @return bool Success */ public static function rollback(): bool { return self::getConnection()->rollback(); } /** * Execute a query and return results * * @param string $sql SQL query with placeholders * @param string $types Parameter types (i=int, s=string, d=double, b=blob) * @param array $params Parameters to bind * @return mysqli_result|bool Query result */ public static function query(string $sql, string $types = '', array $params = []) { $conn = self::getConnection(); if (empty($types) || empty($params)) { return $conn->query($sql); } $stmt = $conn->prepare($sql); if (!$stmt) { throw new Exception("Query preparation failed: " . $conn->error); } $stmt->bind_param($types, ...$params); $stmt->execute(); $result = $stmt->get_result(); $stmt->close(); return $result; } /** * Execute an INSERT/UPDATE/DELETE and return affected rows * * @param string $sql SQL query with placeholders * @param string $types Parameter types * @param array $params Parameters to bind * @return int Affected rows (-1 on failure) */ public static function execute(string $sql, string $types = '', array $params = []): int { $conn = self::getConnection(); $stmt = $conn->prepare($sql); if (!$stmt) { throw new Exception("Query preparation failed: " . $conn->error); } if (!empty($types) && !empty($params)) { $stmt->bind_param($types, ...$params); } if ($stmt->execute()) { $affected = $stmt->affected_rows; $stmt->close(); return $affected; } $error = $stmt->error; $stmt->close(); throw new Exception("Query execution failed: " . $error); } /** * Get the last insert ID * * @return int Last insert ID */ public static function lastInsertId(): int { return self::getConnection()->insert_id; } /** * Escape a string for use in queries (prefer prepared statements) * * @param string $string String to escape * @return string Escaped string */ public static function escape(string $string): string { return self::getConnection()->real_escape_string($string); } }