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];
}
}