philippurbschat.de/home/core/Database.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();
}
}