Geizkragen/lib/db.php

109 lines
5.4 KiB
PHP

<?php
/**
* @file db.php
* @brief Enthält grundlegende Funktionen und Initialisierungen für die Datenbankinteraktion.
*
* @details Diese Datei kapselt den gesamten Prozess des Verbindungsaufbaus zur MySQL/MariaDB-Datenbank.
* Sie nutzt die zentralen Konfigurationseinstellungen (wie Host, Benutzer, Passwort, Datenbankname und Port),
* die in der Datei config.php hinterlegt sind. Durch die Auslagerung in diese Datei wird eine einheitliche
* und zentrale Stelle für das Verbindungsmanagement geschaffen, was die Wartbarkeit, Sicherheit und
* Erweiterbarkeit des Gesamtsystems signifikant verbessert.
*
* Besonderes Augenmerk liegt auf der Fehlerbehandlung beim Verbindungsaufbau: Schlägt dieser fehl,
* so wird der Ausführungskontext sicher terminiert und ein HTTP-Statuscode 500 generiert. Zudem wird
* der Zeichensatz streng konfiguriert (meist utf8mb4), was für die fehlerfreie Verarbeitung von
* Sonderzeichen und Emojis essenziell ist und Schutz vor bestimmten SQL-Injection-Vektoren bietet.
*
* @author Geizkragen Entwicklerteam
* @version 1.1
* @date 2026-04-04
*/
declare(strict_types=1); ///< Erzwingt auf Sprachebene strikte Typenprüfung für Parameter und Rückgabewerte dieses Skripts. Dies verhindert fehleranfällige, implizite automatische Typenumwandlungen.
/**
* @brief Baut eine MySQLi-Datenbankverbindung anhand der zentralen Konfiguration auf und liefert diese zurück.
*
* @details Diese essenzielle Funktion greift auf die zentrale Konfigurationsdatei (config.php) zu,
* lädt deren Parameterstruktur und initialisiert eine neue Instanz der Klasse mysqli, sofern dies
* für den aktuellen Ausführungskontext notwendig ist. Um Performance zu optimieren, nutzt sie
* das Singleton-ähnliche Konzept mittels einer statischen Variablen, sodass die Konfiguration
* (und die Datei-Lese-Operation) nur genau einmal pro Skriptlauf ausgeführt wird.
*
* @attention Bei einem Verbindungsfehler bricht das System das Skript mittels die() hart ab.
* Sensible Datenbankfehler (wie falsche Zugangsdaten) werden dabei absichtlich nicht ausgegeben,
* um so genannte Information Disclosure-Vulnerabilities zu vermeiden. Stattdessen wird lediglich
* der neutrale Text "Datenbankfehler" gezeigt und der HTTP Header auf Status 500 gesetzt.
*
* @b Anwendungsbeispiel:
* @code
* // Verbindung zur Datenbank aufbauen
* $mysqli_connection = db_connect();
*
* // Abfrage ausführen
* $result = $mysqli_connection->query("SELECT * FROM users");
* @endcode
*
* @return mysqli Das erfolgreich aufgebaute und vollständig konfigurierte Datenbankverbindungs-Objekt.
*
* @throws Exception Löst im Hintergrund potenziell Ausnahmen aus, fängt diese durch manuelle Fehlerabfragen ($conn->connect_error) aber sauber ab.
*/
function db_connect(): mysqli
{
/**
* @var array|null $cfg Statische Variable zur Wiederverwendung der Systemkonfiguration.
* Durch das Schlüsselwort 'static' behält diese Variable ihren Wert auch über
* multiple Funktionsaufrufe hinweg und verhindert mehrfaches Einlesen der config.php.
*/
static $cfg;
/// Prüft, ob die Konfiguration im aktuellen Skriptlauf bereits geladen und im Speicher abgelegt wurde.
if ($cfg === null)
{
/**
* @var array{db: array{host:string,port:int,user:string,pass:string,name:string,charset:string}} $cfg
* @brief Bindet die Konfigurationsdaten ein und lädt das rückgegebene Array in die statische Variable $cfg.
* Der Dateipfad wird hierbei relativ und sicher über die magische Konstante __DIR__ dynamisch aufgelöst.
*/
$cfg = require __DIR__ . '/config.php';
}
/// Extrahiert aus Leistungs- und Lesbarkeitsgründen den spezifischen Datenbankbereich der geladenen Gesamt-Konfiguration in die lokale Variable $db.
$db = $cfg['db'];
/// Initialisiert direkt eine neue, native mysqli-Verbindung mit den extrahierten Zugangsdaten (Host, Benutzer, Passwort, DB-Name, Port).
$conn = new mysqli($db['host'], $db['user'], $db['pass'], $db['name'], $db['port']);
/**
* @brief Überprüft unmittelbar, ob beim Versuch des Verbindungsaufbaus durch die MySQLi-Extension ein Fehler aufgetreten ist.
* Wenn der Wert von $conn->connect_error einen String enthält (und somit nicht null ist), war der Verbindungsversuch erfolglos.
*/
if ($conn->connect_error)
{
/**
* @brief Ein fataler Fehler ist aufgetreten: Setzt den HTTP-Response-Statuscode auf 500 (Internal Server Error).
* Dies teilt dem Browser oder API-Konsumenten strukturiert mit, dass der Server ein internes Problem hat.
*/
http_response_code(500);
/**
* @brief Bricht die Ausführung des gesamten PHP-Skripts unwiderruflich ab.
* Aus Sicherheitsgründen wird dem Client lediglich eine maskierte, generelle String-Fehlermeldung ("Datenbankfehler") ausgegeben.
*/
die('Datenbankfehler');
}
/**
* @brief Setzt für die nun erfolgreich initiierte Verbindung ein einheitliches, konfiguriertes Charset.
* Dies ist (insbesondere bei utf8mb4) aus Sicherheits- und Stabilitätsgründen äußerst wichtig, um Encoding-Probleme,
* abgeschnittene Texte und potenzielle SQL-Sicherheitslücken beim Escaping vollständig auszuschließen.
*/
$conn->set_charset($db['charset']);
/// Der Aufbau war erfolgreich. Gibt die einsatzbereite und abgesicherte MySQLi-Klasseninstanz an den aufrufenden Kontext zurück.
return $conn;
}
?>