$type, 'message' => $message]; } /** * Flash-Messages abrufen & löschen * * @return array */ function getFlashes(): array { if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } $messages = $_SESSION[FLASH_SESSION_KEY] ?? []; unset($_SESSION[FLASH_SESSION_KEY]); return $messages; } /* ========================= * Request Helper * ========================= */ /** * POST-Wert holen * * @param string $key * @param mixed $default * @return mixed */ function post(string $key, $default = null) { return $_POST[$key] ?? $default; } /** * GET-Wert holen * * @param string $key * @param mixed $default * @return mixed */ function get(string $key, $default = null) { return $_GET[$key] ?? $default; } /** * Prüfen, ob Request POST ist * * @return bool */ function isPost(): bool { return ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST'; } /* ========================= * Validierung * ========================= */ /** * Prüft auf leeren Wert * * @param mixed $value * @return bool */ function isEmpty($value): bool { if (is_string($value)) { return trim($value) === ''; } return empty($value); } /* ========================= * Pfade / URLs * ========================= */ /** * Baut eine URL zur App * * @param string $path * @return string */ function url(string $path = ''): string { if ($path === '') { $path = '/'; } if (preg_match('~^([a-z]+:)?//~i', $path)) { return $path; } $script = $_SERVER['SCRIPT_NAME'] ?? ''; $baseDir = rtrim(strtr(dirname($script), '\\\\', '/'), '/'); if ($baseDir === '.' || $baseDir === '\\\\') { $baseDir = ''; } $segment = ltrim($path, '/'); $prefix = $baseDir === '' ? '' : $baseDir; if ($segment === '') { return $prefix === '' ? '/' : $prefix; } return ($prefix === '' ? '' : $prefix) . '/' . $segment; } /* ========================= * Debug / Entwicklung * ========================= */ /** * Dump & Die (nur Dev) * * @param mixed $value */ function dd($value): void { echo '
';
    var_dump($value);
    echo '
'; exit; } /** * Zeigt eine sauber gestaltete Fehlerseite für 40x-Status-Codes mit Erklärung. * * @param int $statusCode Client-Error-Status (400–499) * @param string $explanation Optionale Zusatzinfo zur Problemursache * @param string[] $tips Handlungsvorschläge für Benutzer (optional) */ function renderClientError(int $statusCode, string $explanation = '', array $tips = []): void { if ($statusCode < 400 || $statusCode >= 500) { $statusCode = 400; } $reasons = [ 400 => 'Ungültige Anfrage', 401 => 'Nicht authentifiziert', 403 => 'Zugriff verweigert', 404 => 'Seite nicht gefunden', 405 => 'Methode nicht erlaubt', 408 => 'Anfrage abgelaufen', 429 => 'Zu viele Anfragen', ]; $reason = $reasons[$statusCode] ?? 'Clientseitiger Fehler'; $defaultExplanation = match ($statusCode) { 400 => 'Die Anfrage konnte aufgrund fehlender oder falscher Daten nicht verstanden werden.', 401 => 'Bitte melden Sie sich an oder verwenden gültige Zugangsdaten.', 403 => 'Sie besitzen keinen Zugriff auf diesen Bereich.', 404 => 'Die angeforderte Ressource existiert nicht oder wurde verschoben.', 405 => 'Diese Aktion ist auf dem Server nicht erlaubt.', 408 => 'Die Anfrage hat zu lange gedauert; bitte erneut versuchen.', 429 => 'Sie senden zu viele Anfragen in kurzer Zeit.', default => 'Die Anfrage kann nicht verarbeitet werden; überprüfen Sie die Eingaben.', }; $message = $explanation !== '' ? $explanation : $defaultExplanation; http_response_code($statusCode); $css = << Fehler {{code}}
{{status}}

{{code}} · {{reason}}

{{message}}

{{tips}}

Zurück zur Startseite: Dashboard

HTML, [ '{{code}}' => e((string)$statusCode), '{{reason}}' => e($reason), '{{status}}' => e(sprintf('%d Fehler', $statusCode)), '{{message}}' => e($message), '{{css}}' => $css, '{{tips}}' => count($tips) === 0 ? '' : '', ]); echo $rendered; exit; } /* ========================= * Sonstiges * ========================= */ /** * Formatiert Datum/Uhrzeit robust oder gibt Fallback zurueck. * * @param string|null $value * @param string $format * @param string $fallback * @return string */ function formatDateTime(?string $value, string $format = 'd.m.Y H:i', string $fallback = '-'): string { if ($value === null || trim($value) === '') { return $fallback; } $timestamp = strtotime($value); if ($timestamp === false) { return $fallback; } return date($format, $timestamp); } /** * Formatiert Byte-Werte in menschenlesbare Einheit. * * @param int|float $bytes * @param int $precision * @return string */ function formatBytes($bytes, int $precision = 2): string { $value = (float)$bytes; if ($value <= 0) { return '0 B'; } $units = ['B', 'KB', 'MB', 'GB', 'TB']; $power = min((int)floor(log($value, 1024)), count($units) - 1); $scaled = $value / (1024 ** $power); return number_format($scaled, $precision, '.', '') . ' ' . $units[$power]; } /** * Erzeugt eine UUID v4. * * @return string * @throws Exception */ function generateUuidV4(): string { $bytes = random_bytes(16); $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40); $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80); $hex = bin2hex($bytes); return sprintf( '%s-%s-%s-%s-%s', substr($hex, 0, 8), substr($hex, 8, 4), substr($hex, 12, 4), substr($hex, 16, 4), substr($hex, 20, 12) ); } /** * Klemmt eine SVG-Koordinate auf gueltigen Bereich. * * @param float $value * @param float $min * @param float $max * @param int $precision * @return float */ function normalizeSvgCoordinate(float $value, float $min, float $max, int $precision = 2): float { $normalized = max($min, min($max, $value)); return round($normalized, $precision); }