dbh = self::$sharedDbh; $this->driver = self::$sharedDriver; return; } $connectionType = Config::get('DB_CONNECTION', 'mysql'); $host = Config::get('DB_HOST', 'localhost'); $user = Config::get('DB_USER', 'root'); $pass = Config::get('DB_PASS', ''); $dbname = Config::get('DB_NAME', ''); $options = [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC ]; // Explicit SQLite configuration or empty dbname in non-production if ($connectionType === 'sqlite' || (empty($dbname) && Config::get('APP_ENV') !== 'production')) { $this->connectSqlite($options); self::$sharedDbh = $this->dbh; self::$sharedDriver = $this->driver; return; } if (empty($dbname)) { throw new Exception("Keine Datenbank in .env konfiguriert."); } $dsn = 'mysql:host=' . $host . ';dbname=' . $dbname . ';charset=utf8mb4'; try { $this->dbh = new PDO($dsn, $user, $pass, $options); $this->driver = 'mysql'; self::$sharedDbh = $this->dbh; self::$sharedDriver = $this->driver; } catch (PDOException $e) { $this->error = $e->getMessage(); // In local development (macOS / Darwin) or when explicitly configured, fall back to SQLite if (PHP_OS_FAMILY === 'Darwin' || Config::get('APP_ENV') !== 'production' || $connectionType === 'sqlite') { $this->connectSqlite($options); self::$sharedDbh = $this->dbh; self::$sharedDriver = $this->driver; } else { throw new Exception("Datenbank-Verbindungsfehler: " . $e->getMessage()); } } $this->ensureSchema(); } private function connectSqlite(array $options): void { $this->driver = 'sqlite'; $dataDir = __DIR__ . '/../data'; if (!is_dir($dataDir)) { mkdir($dataDir, 0755, true); } $sqlitePath = $dataDir . '/database.sqlite'; $this->dbh = new PDO('sqlite:' . $sqlitePath, null, null, $options); $this->dbh->exec('PRAGMA busy_timeout = 5000;'); $this->dbh->exec('PRAGMA journal_mode = WAL;'); $this->dbh->exec('PRAGMA foreign_keys = ON;'); $this->ensureSchema(); } private function ensureSchema(): void { if (!self::$migrated) { self::$migrated = true; if (class_exists('MigrationService')) { MigrationService::run($this); } } } public function getDriver(): string { return $this->driver; } public function lastInsertId(): string { return $this->dbh->lastInsertId(); } public function query($sql) { $this->stmt = $this->dbh->prepare($sql); } public function bind($param, $value, $type = null) { if (is_null($type)) { switch (true) { case is_int($value): $type = PDO::PARAM_INT; break; case is_bool($value): $type = PDO::PARAM_BOOL; break; case is_null($value): $type = PDO::PARAM_NULL; break; default: $type = PDO::PARAM_STR; } } $this->stmt->bindValue($param, $value, $type); } public function execute() { return $this->stmt->execute(); } public function resultSet() { $this->execute(); $result = $this->stmt->fetchAll(); $this->stmt->closeCursor(); return $result; } public function single() { $this->execute(); $result = $this->stmt->fetch(); $this->stmt->closeCursor(); return $result; } public function rowCount() { return $this->stmt->rowCount(); } }