126 lines
3.4 KiB
PHP
126 lines
3.4 KiB
PHP
|
<?php
|
||
|
// RouteInfo.php
|
||
|
// Created: 2022-01-20
|
||
|
// Updated: 2022-02-27
|
||
|
|
||
|
namespace Index\Routing;
|
||
|
|
||
|
use InvalidArgumentException;
|
||
|
use UnexpectedValueException;
|
||
|
|
||
|
/**
|
||
|
* Represents a node in the router structure.
|
||
|
*
|
||
|
* @internal
|
||
|
*/
|
||
|
class RouteInfo {
|
||
|
private array $children = [];
|
||
|
private array $methods = [];
|
||
|
private array $middlewares = [];
|
||
|
private ?RouteInfo $dynamicChild = null;
|
||
|
|
||
|
private function getChild(string $path, ?string &$next = null): RouteInfo {
|
||
|
$parts = explode('/', $path, 2);
|
||
|
$name = '_' . $parts[0];
|
||
|
|
||
|
if(str_starts_with($parts[0], ':'))
|
||
|
$child = $this->dynamicChild ?? ($this->dynamicChild = new RouteInfo);
|
||
|
else
|
||
|
$child = $this->children[$name] ?? ($this->children[$name] = new RouteInfo);
|
||
|
|
||
|
$next = $parts[1] ?? '';
|
||
|
|
||
|
return $child;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal
|
||
|
*/
|
||
|
public function mergeRoute(string $path, RouteInfo $route): void {
|
||
|
if($path === '') {
|
||
|
$this->children = array_merge($this->children, $route->children);
|
||
|
$this->methods = array_merge($this->methods, $route->methods);
|
||
|
$this->middlewares = array_merge($this->middlewares, $route->middlewares);
|
||
|
if($this->dynamicChild === null)
|
||
|
$this->dynamicChild = $route->dynamicChild;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$this->getChild($path, $next)->mergeRoute($next, $route);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal
|
||
|
*/
|
||
|
public function addMiddleware(string $path, callable $handler): void {
|
||
|
if($path === '') {
|
||
|
$this->middlewares[] = $handler;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$this->getChild($path, $next)->addMiddleware($next, $handler);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal
|
||
|
*/
|
||
|
public function addMethod(string $method, string $path, callable $handler): void {
|
||
|
if($path === '') {
|
||
|
$this->methods[strtolower($method)] = $handler;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$this->getChild($path, $next)->addMethod($method, $next, $handler);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @internal
|
||
|
*/
|
||
|
public function resolve(string $method, string $path, array $args = [], array $callables = []): RouteCallable {
|
||
|
if($path === '') {
|
||
|
$method = strtolower($method);
|
||
|
$handlers = [];
|
||
|
|
||
|
if(isset($this->methods[$method])) {
|
||
|
$handlers[] = $this->methods[$method];
|
||
|
} else {
|
||
|
if($method !== 'options') {
|
||
|
if(empty($this->methods))
|
||
|
throw new RoutePathNotFoundException;
|
||
|
throw new RouteMethodNotSupportedException;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return new RouteCallable(
|
||
|
array_merge($callables, $this->middlewares, $handlers),
|
||
|
$args
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$parts = explode('/', $path, 2);
|
||
|
$name = $parts[0];
|
||
|
$mName = '_' . $name;
|
||
|
|
||
|
foreach($this->children as $cName => $cObj)
|
||
|
if($cName === $mName) {
|
||
|
$child = $cObj;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if(!isset($child)) {
|
||
|
$args[] = $name;
|
||
|
$child = $this->dynamicChild;
|
||
|
}
|
||
|
|
||
|
if($child === null)
|
||
|
throw new RoutePathNotFoundException;
|
||
|
|
||
|
return $child->resolve(
|
||
|
$method,
|
||
|
$parts[1] ?? '',
|
||
|
$args,
|
||
|
array_merge($callables, $this->middlewares)
|
||
|
);
|
||
|
}
|
||
|
}
|