Files
deskhub/DeskHub-Webseite/includes/db.php
T
2026-11-04 07:55:19 +00:00

123 lines
5.0 KiB
PHP

<?php
/**
* Datenbankverbindung (PDO).
* Unterstützt sowohl MySQL (Produktivbetrieb) als auch SQLite (lokale Tests),
* gesteuert über config/config.php -> db.driver
*/
require_once __DIR__ . '/../database/sqlite_schema.php';
function config(): array
{
static $config = null;
if ($config === null) {
$config = require __DIR__ . '/../config/config.php';
// Nur für Tests: einzelne Werte überschreiben (wirkt ausschließlich mit DESKHUB_TESTING=1)
if (getenv('DESKHUB_TESTING') === '1' && ($extra = json_decode((string) getenv('DESKHUB_CONFIG_JSON'), true)) && is_array($extra)) {
$config = array_replace_recursive($config, $extra);
}
// Nur für Tests: Datenbankangaben (zum Beispiel eine MySQL-Testdatenbank) überschreiben alles andere, auch wenn der Test selbst DESKHUB_CONFIG_JSON setzt
if (getenv('DESKHUB_TESTING') === '1' && ($testDbJson = json_decode((string) getenv('DESKHUB_TEST_DB_JSON'), true)) && is_array($testDbJson)) {
$config['db'] = array_replace($config['db'] ?? [], $testDbJson);
}
// Nur für die Testläufe (tests/run_all.php setzt DESKHUB_TESTS_FORCE_SQLITE): nie die in config/config.php eingetragene Datenbank
// (zum Beispiel die Produktiv-MySQL) benutzen, sondern SQLite. In Test-Webservern nur mit einer vom Test gesetzten Datenbankdatei.
if (getenv('DESKHUB_TESTS_FORCE_SQLITE') === '1') {
$testDb = getenv('DESKHUB_SQLITE_PATH');
if (PHP_SAPI === 'cli' || ($testDb !== false && $testDb !== '' && $testDb !== ':memory:')) {
$config['db']['driver'] = 'sqlite';
$config['db']['sqlite_path'] = $testDb !== false && $testDb !== '' ? $testDb : ':memory:';
}
}
}
return $config;
}
/**
* Die Datenbank ist nicht eingerichtet: Mit vorhandenem Installer dorthin weiterleiten, sonst eine verständliche Meldung.
* Auf der Kommandozeile (Cron, Skripte) wird ein Fehler geworfen.
*/
function app_not_installed(): never
{
$message = 'DeskHub ist noch nicht eingerichtet: Die Datenbank fehlt. Bitte die Installation unter /install/ abschließen.';
if (PHP_SAPI === 'cli') throw new RuntimeException($message);
$installerOpen = is_file(__DIR__ . '/../install/index.php') && !is_file(__DIR__ . '/../install/.installed');
if ($installerOpen && !headers_sent()) {
header('Location: /install/');
exit;
}
http_response_code(503);
header('Content-Type: text/plain; charset=utf-8');
exit($message . "
");
}
function get_db(): PDO
{
static $pdo = null;
if ($pdo !== null) {
return $pdo;
}
$cfg = config()['db'];
if ($cfg['driver'] === 'sqlite') {
$dbPath = $cfg['sqlite_path'];
$needInit = !file_exists($dbPath) || filesize($dbPath) === 0;
// Eine SQLite-Datenbank wird nie nebenbei angelegt (z. B. durch den ersten Seitenaufruf mit einer mitgelieferten Konfiguration):
// Das macht allein der Installer, wenn SQLite gewählt wurde. Nur Tests und Entwickler, die den Pfad ausdrücklich per
// DESKHUB_SQLITE_PATH angeben, dürfen eine neue Datenbank erzeugen lassen.
if ($needInit && $dbPath !== ':memory:' && (string) getenv('DESKHUB_SQLITE_PATH') === '') {
app_not_installed();
}
$pdo = new PDO('sqlite:' . $dbPath);
$pdo->exec('PRAGMA journal_mode = WAL;');
$pdo->exec('PRAGMA foreign_keys = ON;');
if ($needInit) {
$schemaFile = __DIR__ . '/../database/schema.sql';
if (!file_exists($schemaFile)) {
throw new \RuntimeException("Schema-Datei nicht gefunden: $schemaFile");
}
$sql = file_get_contents($schemaFile);
$pdo->exec(convert_schema_to_sqlite($sql));
}
} else {
$dsn = sprintf('mysql:host=%s;dbname=%s;charset=utf8mb4', $cfg['host'], $cfg['name']);
$pdo = new PDO($dsn, $cfg['user'], $cfg['pass'], [
PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8mb4",
]);
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// Ausstehende Schema-Migrationen automatisch einspielen.
// Bereits angewendete werden übersprungen → Einstellungen bleiben erhalten.
$migrateFile = __DIR__ . '/../database/migrate.php';
if (file_exists($migrateFile)) {
require_once $migrateFile;
$migrationResult = run_migrations($pdo);
if ($migrationResult['errors']) {
throw new RuntimeException('Datenbankmigration fehlgeschlagen: ' . implode('; ', $migrationResult['errors']));
}
}
return $pdo;
}
/** Liefert NOW() als DATETIME-String, kompatibel zu beiden Treibern */
function db_now(): string
{
return date('Y-m-d H:i:s');
}
/** SQL-Datum-Differenz (Stunden addieren), kompatibel zu beiden Treibern -> wird in PHP berechnet */
function add_hours(string $datetime, int $hours): string
{
$ts = strtotime($datetime) + ($hours * 3600);
return date('Y-m-d H:i:s', $ts);
}