index/src/Http/Routing/HttpRouter.php

108 lines
3.2 KiB
PHP

<?php
// HttpRouter.php
// Created: 2024-03-28
// Updated: 2024-03-28
namespace Index\Http\Routing;
use stdClass;
use InvalidArgumentException;
use RuntimeException;
class HttpRouter implements IRouter {
use RouterTrait;
private array $middlewares = [];
private array $staticRoutes = [];
private array $dynamicRoutes = [];
public function scopeTo(string $prefix): IRouter {
return new ScopedRouter($this, $prefix);
}
private static function preparePath(string $path, bool $prefixMatch): string|false {
// this sucks lol
if(!str_contains($path, '(') || !str_contains($path, ')'))
return false;
return sprintf('#^%s%s#su', $path, $prefixMatch ? '' : '$');
}
public function use(string $path, callable $handler): void {
$this->middlewares[] = $mwInfo = new stdClass;
$mwInfo->handler = $handler;
$prepared = self::preparePath($path, true);
$mwInfo->dynamic = $prepared !== false;
if($mwInfo->dynamic)
$mwInfo->match = $prepared;
else
$mwInfo->prefix = $path;
}
public function add(string $method, string $path, callable $handler): void {
if($method === '')
throw new InvalidArgumentException('$method may not be empty');
$method = strtoupper($method);
if(trim($method) !== $method)
throw new InvalidArgumentException('$method may start or end with whitespace');
$prepared = self::preparePath($path, false);
if($prepared === false) {
if(array_key_exists($path, $this->staticRoutes))
$this->staticRoutes[$path][$method] = $handler;
else
$this->staticRoutes[$path] = [$method => $handler];
} else {
if(array_key_exists($prepared, $this->dynamicRoutes))
$this->dynamicRoutes[$prepared][$method] = $handler;
else
$this->dynamicRoutes[$prepared] = [$method => $handler];
}
}
public function resolve(string $method, string $path): ResolvedRouteInfo {
$middlewares = [];
foreach($this->middlewares as $mwInfo) {
if($mwInfo->dynamic) {
if(preg_match($mwInfo->match, $path, $args) !== 1)
continue;
array_shift($args);
} else {
if(!str_starts_with($path, $mwInfo->prefix))
continue;
$args = [];
}
$middlewares[] = [$mwInfo->handler, $args];
}
$methods = [];
$handler = null;
$args = [];
if(array_key_exists($path, $this->staticRoutes)) {
$methods = $this->staticRoutes[$path];
} else {
foreach($this->dynamicRoutes as $rPattern => $rMethods)
if(preg_match($rPattern, $path, $args) === 1) {
$methods = $rMethods;
array_shift($args);
break;
}
}
$method = strtoupper($method);
if(array_key_exists($method, $methods))
$handler = $methods[$method];
return new ResolvedRouteInfo($middlewares, array_keys($methods), $handler, $args);
}
}