124 lines
3.5 KiB
PHP
124 lines
3.5 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace App;
|
|
|
|
final class Database
|
|
{
|
|
public static function createPdo(Config $config): ?\PDO
|
|
{
|
|
if (!$config->dbEnabled) {
|
|
return null;
|
|
}
|
|
|
|
$db = $config->db;
|
|
$driver = (string)($db['driver'] ?? '');
|
|
|
|
if ($driver === '') {
|
|
throw new \RuntimeException('DB enabled but config/db.php missing "driver"');
|
|
}
|
|
|
|
$dsn = match ($driver) {
|
|
'mysql' => self::buildMysqlDsn($db),
|
|
'pgsql' => self::buildPgsqlDsn($db),
|
|
'sqlite' => self::buildSqliteDsn($db),
|
|
default => throw new \RuntimeException('Unsupported PDO driver: ' . $driver),
|
|
};
|
|
|
|
try {
|
|
$pdo = new \PDO(
|
|
$dsn,
|
|
// sqlite braucht user/pass nicht, PDO ignoriert es aber; wir geben leer zurück
|
|
(string)($db['user'] ?? ''),
|
|
(string)($db['password'] ?? ''),
|
|
(array)($db['options'] ?? [])
|
|
);
|
|
|
|
// Optional: PostgreSQL schema/search_path setzen
|
|
if ($driver === 'pgsql' && !empty($db['schema'])) {
|
|
// Minimaler Schutz gegen Injection über schema
|
|
$schema = preg_replace('/[^a-zA-Z0-9_]/', '', (string)$db['schema']);
|
|
if ($schema !== '') {
|
|
$pdo->exec('SET search_path TO ' . $schema);
|
|
}
|
|
}
|
|
|
|
return $pdo;
|
|
} catch (\PDOException $e) {
|
|
// In Prod würdest du loggen; hier minimal
|
|
http_response_code(500);
|
|
echo 'Database connection error.';
|
|
exit;
|
|
}
|
|
}
|
|
|
|
private static function buildMysqlDsn(array $db): string
|
|
{
|
|
if (empty($db['dbname'])) {
|
|
throw new \RuntimeException('MySQL config missing "dbname"');
|
|
}
|
|
|
|
$charset = (string)($db['charset'] ?? 'utf8mb4');
|
|
|
|
// Unix socket takes precedence
|
|
if (!empty($db['unix_socket'])) {
|
|
return sprintf(
|
|
'mysql:unix_socket=%s;dbname=%s;charset=%s',
|
|
(string)$db['unix_socket'],
|
|
(string)$db['dbname'],
|
|
$charset
|
|
);
|
|
}
|
|
|
|
$host = (string)($db['host'] ?? 'localhost');
|
|
$port = (int)($db['port'] ?? 3306);
|
|
|
|
return sprintf(
|
|
'mysql:host=%s;port=%d;dbname=%s;charset=%s',
|
|
$host,
|
|
$port,
|
|
(string)$db['dbname'],
|
|
$charset
|
|
);
|
|
}
|
|
|
|
private static function buildPgsqlDsn(array $db): string
|
|
{
|
|
if (empty($db['dbname'])) {
|
|
throw new \RuntimeException('PostgreSQL config missing "dbname"');
|
|
}
|
|
|
|
$host = (string)($db['host'] ?? 'localhost');
|
|
$port = (int)($db['port'] ?? 5432);
|
|
|
|
// Hinweis: charset gehört bei pgsql nicht in den DSN
|
|
return sprintf(
|
|
'pgsql:host=%s;port=%d;dbname=%s',
|
|
$host,
|
|
$port,
|
|
(string)$db['dbname']
|
|
);
|
|
}
|
|
|
|
private static function buildSqliteDsn(array $db): string
|
|
{
|
|
// SQLite kann :memory: oder einen Pfad nutzen
|
|
$path = (string)($db['path'] ?? '');
|
|
|
|
if ($path === '') {
|
|
// Default: Memory-DB
|
|
$path = ':memory:';
|
|
}
|
|
|
|
// Wenn es ein Pfad ist, stelle sicher, dass das Verzeichnis existiert.
|
|
if ($path !== ':memory:') {
|
|
$dir = \dirname($path);
|
|
if ($dir && !is_dir($dir)) {
|
|
@mkdir($dir, 0775, true);
|
|
}
|
|
}
|
|
|
|
return 'sqlite:' . $path;
|
|
}
|
|
}
|