← Code-Übersicht

Router.php

Pfad: src/Infrastructure/Http/Router.php

Ext: php

Größe: 2695 Bytes

Geändert: 2026-07-15 21:48:56+02

<?php
declare(strict_types=1);

namespace Demo\Infrastructure\Http;

/**
 * Whitelist-Router mit Methode und optionalem Pfadparameter ({name}).
 * Unbekannter Pfad -> 404, bekannter Pfad mit falscher Methode -> 405.
 */
final class Router
{
    /** @var list<array{method:string,regex:string,params:list<string>,handler:callable}> */
    private array $routes = [];

    /**
     * Registriert einen Handler fuer Methode und Pfadmuster.
     *
     * @param string   $method  HTTP-Methode, z.B. GET oder POST.
     * @param string   $path    Muster, z.B. /doku/{slug}.
     * @param callable $handler Erhaelt array<string,string> $params, liefert Response.
     */
    public function add(string $method, string $path, callable $handler): void
    {
        [$regex, $params] = $this->compile($path);
        $this->routes[] = [
            'method' => strtoupper($method),
            'regex' => $regex,
            'params' => $params,
            'handler' => $handler,
        ];
    }

    /**
     * Loest Methode und Pfad auf und liefert die passende Antwort.
     *
     * @param string $method Angefragte HTTP-Methode.
     * @param string $path   Angefragter Pfad ohne Query.
     * @return Response Antwort des Handlers, 404 oder 405.
     */
    public function dispatch(string $method, string $path): Response
    {
        $p = '/' . trim($path, '/');
        $method = strtoupper($method);
        $pfadGefunden = false;
        foreach ($this->routes as $route) {
            if (preg_match($route['regex'], $p, $treffer) !== 1) {
                continue;
            }
            $pfadGefunden = true;
            if ($route['method'] !== $method) {
                continue;
            }
            $args = [];
            foreach ($route['params'] as $name) {
                $args[$name] = $treffer[$name] ?? '';
            }

            return ($route['handler'])($args);
        }

        return $pfadGefunden
            ? new Response(405, 'text/plain; charset=UTF-8', 'Method Not Allowed')
            : new Response(404, 'text/plain; charset=UTF-8', 'Not Found');
    }

    /**
     * Uebersetzt ein Pfadmuster in Regex und Parameternamen.
     *
     * @param string $path Muster mit optionalen {name}-Platzhaltern.
     * @return array{0:string,1:list<string>} Regex und Parameternamen.
     */
    private function compile(string $path): array
    {
        $p = '/' . trim($path, '/');
        $params = [];
        if (preg_match_all('/\{([a-z_]+)\}/', $p, $treffer) > 0) {
            $params = $treffer[1];
        }
        $regex = (string) preg_replace('/\{([a-z_]+)\}/', '(?P<$1>[^/]+)', $p);

        return ['#^' . $regex . '$#', $params];
    }
}