131 lines
No EOL
4.1 KiB
PHP
131 lines
No EOL
4.1 KiB
PHP
<?php
|
|
class Database {
|
|
private static $sharedDbh = null;
|
|
private static $sharedDriver = 'mysql';
|
|
private $dbh;
|
|
private $stmt;
|
|
private $error;
|
|
private $driver = 'mysql';
|
|
private static $migrated = false;
|
|
|
|
public function __construct() {
|
|
if (self::$sharedDbh !== null) {
|
|
$this->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();
|
|
}
|
|
} |