108 lines
3.2 KiB
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);
|
|
}
|
|
}
|