PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, PDO::ATTR_STRINGIFY_FETCHES => false, ]; try { $pdo = new PDO($dsn, (string)$dbConfig['username'], (string)($dbConfig['password'] ?? ''), $options); $pdo->exec("SET time_zone = '+00:00'"); } catch (PDOException $e) { // Die Originalmeldung kann Host und Benutzername enthalten und // gehoert deshalb ins Log, nicht in die Exception-Kette nach aussen. Logger::error('Datenbankverbindung fehlgeschlagen', ['error' => $e->getMessage()]); throw new RuntimeException('Datenbankverbindung fehlgeschlagen.', 0, $e); } self::$instance = $pdo; return self::$instance; } /** Alias fuer init(); historisch an mehreren Stellen verwendet. */ public static function connect(array $config = []): PDO { return self::init($config); } public static function getInstance(): PDO { return self::$instance ?? self::init(); } /** * Fuehrt einen Callback in einer Transaktion aus. * Verschachtelte Aufrufe laufen in der bereits offenen Transaktion mit. * * @template T * @param callable(PDO):T $callback * @return T */ public static function transaction(callable $callback) { $pdo = self::getInstance(); if ($pdo->inTransaction()) { return $callback($pdo); } $pdo->beginTransaction(); try { $result = $callback($pdo); $pdo->commit(); return $result; } catch (\Throwable $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } throw $e; } } /** Aktueller UTC-Zeitstempel im MySQL-DATETIME-Format. */ public static function nowUtc(): string { return gmdate('Y-m-d H:i:s'); } }