Geizkragen/404.php

336 lines
12 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* @file 404.php
* @brief Fehlerseite für nicht gefundene Dokumente (HTTP 404 Not Found)
*
* @details Diese Datei wird aufgerufen, wenn ein Benutzer eine URL aufruft,
* die auf dem Server nicht existiert. Sie sendet den korrekten HTTP-Statuscode 404 an den Browser,
* liest die angeforderte URI aus und bietet dem Benutzer benutzerfreundliche Möglichkeiten,
* zurück zur Startseite oder zur vorherigen Seite zu navigieren. Ein ansprechendes Design
* mit Glitch-Effekten macht die Fehlerseite visuell interessant.
*
* @author Fabian Schieder / Geizkragen Team
* @version 1.0
*/
// Wichtig: Echten 404-Status setzen, damit Suchmaschinen und Browser erkennen, dass die Seite nicht existiert.
// !!! Funktioniert in der Produktion nur zuverlässig (z.B. auf geizkragen.fabianschieder.com),
// wenn die Fehlerseite im VirtualHost oder der .htaccess entsprechend für 404-Fehler konfiguriert ist !!!
http_response_code(404);
// Request-Daten absichern, um XSS (Cross-Site Scripting) Angriffe bei der Ausgabe zu verhindern.
// htmlspecialchars wandelt spezielle Zeichen (<, >, &, ", ') in ungefährliche HTML-Entitäten um.
$requestUri = htmlspecialchars($_SERVER['REQUEST_URI'] ?? '', ENT_QUOTES, 'UTF-8');
$method = htmlspecialchars($_SERVER['REQUEST_METHOD'] ?? '', ENT_QUOTES, 'UTF-8');
// Optional: Einfaches Logging der 404-Fehler für administrative Zwecke.
// Schreibt die IP-Adresse des Clients, die HTTP-Methode und die angeforderte fehlerhafte URI in das PHP Error-Log.
error_log("[404] " . ($_SERVER['REMOTE_ADDR'] ?? '') . " $method $requestUri");
?>
<!DOCTYPE html>
<html lang="de">
<head>
<!-- Metadaten zur Zeichenkodierung und für ein responsives Layout -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Einbindung des Favicons -->
<link rel="icon" href="/assets/images/favicon.ico" sizes="any">
<!-- Einbindung des globalen Stylesheets -->
<link rel="stylesheet" href="/style.css">
<!-- Titel der Seite, der im Browser-Tab angezeigt wird -->
<title>404 Seite nicht gefunden | Geizkragen</title>
</head>
<body>
<!-- Hauptinhaltsbereich der Fehlerseite -->
<main>
<!-- Container für die Zentrierung und Ausrichtung der Inhalte -->
<div class="container" style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
text-align: center;
padding-block: 4rem;
">
<!--
404 Glitch-Zahl:
Ein dekoratives Element, das die Zahl 404 groß und mit einem visuellen Störungs-Effekt (Glitch) darstellt.
Das Attribut aria-hidden="true" versteckt es vor Screenreadern, da es rein dekorativ ist.
-->
<div class="error-code" aria-hidden="true">404</div>
<!--
Error Card:
Eine visuelle Karte, die den Text, das Icon und die Navigations-Aktionen strukturiert zusammenfasst.
-->
<div class="error-card">
<!-- Icon-Bereich in der Fehlerkarte -->
<div class="error-card__icon">
<!-- SVG-Icon: Ein durchgestrichener Kreis zur symbolischen Darstellung eines Fehlers -->
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="28" height="28">
<circle cx="11" cy="11" r="8"/>
<path d="M21 21l-4.35-4.35"/>
<line x1="8" y1="11" x2="14" y2="11"/>
</svg>
</div>
<!-- Überschrift für die Fehlerkarte -->
<h1 class="error-card__title">Seite nicht gefunden</h1>
<!-- Erklärender Text für den Benutzer -->
<p class="error-card__text">
Die Seite, die du suchst, existiert leider nicht oder wurde verschoben.
</p>
<!-- Ausgabe des angeforderten Pfads, der zum Fehler geführt hat -->
<div class="error-card__path"><?php echo $requestUri; ?></div>
<!-- Container für die Navigationsbuttons -->
<div class="error-card__actions">
<!-- Button: Zurück zur Startseite -->
<a href="/index.php" class="btn btn--primary">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="18" height="18">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
Zur Startseite
</a>
<!-- Button: Zurück zur vorherigen Seite im Browser-Verlauf -->
<button onclick="history.back()" class="btn btn--ghost">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" width="18" height="18">
<line x1="19" y1="12" x2="5" y2="12"/>
<polyline points="12 19 5 12 12 5"/>
</svg>
Zurück
</button>
</div>
</div>
</div>
</main>
<!-- Inlined CSS-Bereich für das spezifische Styling der 404-Komponenten -->
<style>
/**
* @brief Styling für die 404 Glitch-Zahl
* @details Definiert eine große, animierte Textdarstellung mit einem Farbgradienten.
*/
/* ── 404 Glitch-Zahl ── */
.error-code {
font-size: clamp(7rem, 18vw, 12rem);
font-weight: 900;
line-height: 1;
letter-spacing: -0.04em;
/* Lineares Farbverlaufs-Hintergrundbild für den Text */
background: linear-gradient(135deg, var(--color-primary), #4f46e5, var(--color-accent));
background-size: 200% 200%;
/* Wendet den Hintergrund nur auf den Text an (Webkit und Standard) */
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
/* Animation zur Verschiebung des Gradienten */
animation: gradientShift 4s ease-in-out infinite;
margin-bottom: 1.5rem;
position: relative;
display: inline-block;
}
/**
* @brief Pseudo-Elemente für den Glitch-Tearing-Effekt
* @details Erstellt Kopien des Textes, die beschnitten und verschoben werden.
*/
.error-code::before,
.error-code::after {
content: '404';
position: absolute;
top: 0; left: 0; right: 0;
overflow: hidden;
background: inherit;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Oberer Teil des Glitches */
.error-code::before {
clip-path: inset(0 0 65% 0);
animation: glitch1 3s infinite linear alternate-reverse;
}
/* Unterer Teil des Glitches */
.error-code::after {
clip-path: inset(65% 0 0 0);
animation: glitch2 3s infinite linear alternate-reverse;
}
/** Keyframes-Animationen für den wackeligen Glitch-Effekt */
@keyframes glitch1 {
0%,92% { transform: translate(0); }
93% { transform: translate(-6px,-2px); }
94% { transform: translate(3px,1px); }
95% { transform: translate(-2px,2px); }
96%,100%{ transform: translate(0); }
}
@keyframes glitch2 {
0%,90% { transform: translate(0); }
91% { transform: translate(4px,2px); }
92% { transform: translate(-5px,-1px); }
93% { transform: translate(2px,-2px); }
94%,100%{ transform: translate(0); }
}
/**
* @brief Styling für die Error-Karten-Komponente (Glassmorphism-Effekt)
*/
/* ── Card ── */
.error-card {
background: rgba(31, 41, 55, 0.55);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-xl);
padding: 2.5rem 2.5rem 2rem;
max-width: 520px;
width: 100%;
box-shadow: var(--shadow-md), 0 0 60px var(--color-primary-glow);
animation: fadeInUp 0.6s ease-out 0.1s both;
}
/* Styling für den Kreis mit dem Icon in der Karte */
.error-card__icon {
width: 56px;
height: 56px;
margin: 0 auto 1.25rem;
background: var(--color-primary-soft);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-primary);
animation: pulse-glow 3s ease-in-out infinite;
}
/* Typografie für den Karten-Titel */
.error-card__title {
font-size: 1.4rem;
font-weight: 800;
color: var(--text-primary);
margin-bottom: 0.6rem;
}
/* Typografie für den Info-Text */
.error-card__text {
color: var(--text-muted);
font-size: 0.95rem;
line-height: 1.6;
margin-bottom: 0.25rem;
}
/* Darstellung des Pfad-Textes im Monospace-Look */
.error-card__path {
display: inline-block;
background: var(--color-primary-soft);
border: 1px solid rgba(39, 74, 151, 0.25);
color: #6b8fd8;
font-family: 'Courier New', Courier, monospace;
font-size: 0.82rem;
font-weight: 600;
padding: 0.35rem 0.9rem;
border-radius: var(--radius-md);
margin: 1rem 0 1.5rem;
word-break: break-all;
max-width: 100%;
}
/**
* @brief Layout und Styling der Aktions-Buttons
*/
/* ── Buttons ── */
.error-card__actions {
display: flex;
gap: 0.75rem; /* Abstand zwischen den Buttons */
justify-content: center;
flex-wrap: wrap; /* Erlaubt Umbruch bei kleinen Bildschirmen */
}
/* Basis-Klasse für Buttons mit Flex-Eigenschaften und Transitionen */
.btn {
display: inline-flex;
align-items: center;
gap: 0.45rem;
padding: 0.7rem 1.4rem;
border-radius: var(--radius-md);
font-size: 0.9rem;
font-weight: 600;
font-family: var(--font-family);
text-decoration: none;
cursor: pointer;
border: none;
transition: all var(--transition-smooth);
}
/* Haupt-Button (Primäraktion) */
.btn--primary {
background: var(--color-primary);
color: #fff;
box-shadow: 0 4px 14px var(--color-primary-glow);
}
/* Hover-Zustand des Haupt-Buttons */
.btn--primary:hover {
background: var(--color-primary-hover);
transform: translateY(-2px); /* Hebe-Effekt */
box-shadow: 0 8px 24px var(--color-primary-glow);
color: #fff;
}
/* Sekundärer Ghost-Button (Transparenter Hintergrund) */
.btn--ghost {
background: rgba(255,255,255,0.04);
color: var(--text-muted);
border: 1px solid var(--border-subtle);
}
/* Hover-Zustand des Ghost-Buttons */
.btn--ghost:hover {
background: rgba(255,255,255,0.09);
color: var(--text-primary);
transform: translateY(-2px);
}
/**
* @brief Responsive Anpassungen (Media Queries)
* @details Passt das Layout auf kleineren Bildschirmen (z.B. Mobilgeräten) an.
*/
/* ── Responsive ── */
@media (max-width: 480px) {
/* Verringerte Polsterung der Karte für kleine Screens */
.error-card {
padding: 1.8rem 1.25rem 1.5rem;
}
/* Stapeln der Buttons untereinander */
.error-card__actions {
flex-direction: column;
}
/* Buttons füllen die gesamte Breite aus und zentrieren ihren Inhalt */
.btn {
justify-content: center;
width: 100%;
}
}
</style>
</body>
</html>