routen[] = [ 'methode' => strtoupper($methode), 'muster' => $muster, 'handler' => $handler, ]; } public function get(string $muster, callable $handler): void { $this->add('GET', $muster, $handler); } public function post(string $muster, callable $handler): void { $this->add('POST', $muster, $handler); } public function dispatch(): void { $methode = strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET'); // Formulare können die Methode über ein verstecktes Feld überschreiben. if ($methode === 'POST') { $override = $_POST['_method'] ?? $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] ?? null; if ($override) { $methode = strtoupper((string)$override); } } $pfad = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/'; $pfad = '/' . trim($pfad, '/'); // Jeder verändernde Request braucht ein gültiges CSRF-Token. if (in_array($methode, ['POST', 'PUT', 'DELETE', 'PATCH'], true)) { Auth::pruefeCsrfToken((string)($_POST['csrf_token'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '')); } foreach ($this->routen as $route) { if ($route['methode'] !== $methode) { continue; } $params = self::passt($route['muster'], $pfad); if ($params !== null) { ($route['handler'])($params); return; } } http_response_code(404); (new View())->render('layout/404.php', ['pfad' => $pfad]); } /** Übersetzt `/konten/{id}` in einen regulären Ausdruck mit benannten Gruppen. */ public static function passt(string $muster, string $pfad): ?array { $regex = preg_replace('/\{(\w+)\}/', '(?P<$1>[^/]+)', $muster); if (preg_match('#^' . $regex . '$#', $pfad, $treffer)) { return array_filter($treffer, 'is_string', ARRAY_FILTER_USE_KEY); } return null; } }