fehlerseiten

This commit is contained in:
2026-02-13 11:03:51 +01:00
parent 52746508fa
commit 8ee3252c51
2 changed files with 90 additions and 3 deletions

View File

@@ -206,6 +206,93 @@ function dd($value): void
exit;
}
/**
* Zeigt eine sauber gestaltete Fehlerseite für 40x-Status-Codes mit Erklärung.
*
* @param int $statusCode Client-Error-Status (400499)
* @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 = <<<CSS
body { font-family: 'Segoe UI', system-ui, sans-serif; margin: 0; background: #0b1220; color: #e0e7ff; }
.page { min-height: 100vh; display: flex; justify-content: center; align-items: center; padding: 2rem; }
.card { max-width: 640px; background: linear-gradient(145deg, #16213d, #0d0f1f); border: 1px solid rgba(224,231,255,0.2); border-radius: 1rem; box-shadow: 0 20px 45px rgba(0,0,0,0.45); padding: 2rem; }
h1 { margin: 0 0 0.5rem; font-size: clamp(2.5rem, 3vw, 3.5rem); }
p { margin: 0 0 1rem; line-height: 1.6; color: rgba(224,231,255,0.9); }
.badge { display: inline-flex; align-items: center; padding: 0.25rem 0.75rem; border-radius: 999px; font-size: 0.85rem; background: rgba(255,255,255,0.08); color: #a5b4fc; margin-bottom: 1rem; }
ul { margin: 1rem 0 0; padding-left: 1.25rem; color: rgba(224,231,255,0.85); }
a { color: #7dd3fc; text-decoration: none; }
a:hover { text-decoration: underline; }
CSS;
$rendered = strtr(<<<'HTML'
<!doctype html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Fehler {{code}}</title>
<style>{{css}}</style>
</head>
<body>
<div class="page">
<section class="card">
<div class="badge">{{status}}</div>
<h1>{{code}} · {{reason}}</h1>
<p>{{message}}</p>
{{tips}}
<p>Zurück zur Startseite: <a href="/">Dashboard</a></p>
</section>
</div>
</body>
</html>
HTML, [
'{{code}}' => e((string)$statusCode),
'{{reason}}' => e($reason),
'{{status}}' => e(sprintf('%d Fehler', $statusCode)),
'{{message}}' => e($message),
'{{css}}' => $css,
'{{tips}}' => count($tips) === 0
? ''
: '<ul>' . implode('', array_map(fn($tip) => '<li>' . e($tip) . '</li>', $tips)) . '</ul>',
]);
echo $rendered;
exit;
}
/* =========================
* Sonstiges
* ========================= */