Files
kiri-router/src/RouterCollector.php
T

332 lines
7.7 KiB
PHP
Raw Normal View History

2023-04-15 23:29:27 +08:00
<?php
2023-04-16 01:24:30 +08:00
declare(strict_types=1);
2023-04-15 23:31:16 +08:00
namespace Kiri\Router;
2023-04-15 23:29:27 +08:00
use Closure;
use Exception;
use Kiri;
2023-04-15 23:31:16 +08:00
use Kiri\Router\Base\NotFoundController;
2023-04-16 01:24:30 +08:00
use Kiri\Router\Constrict\RequestMethod;
2023-10-18 10:37:41 +08:00
use Kiri\Di\Inject\Container;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use Psr\Container\NotFoundExceptionInterface;
2023-04-15 23:29:27 +08:00
use ReflectionException;
2023-12-03 01:26:55 +08:00
use ReflectionMethod;
2023-04-15 23:29:27 +08:00
use Throwable;
use Traversable;
2023-04-15 23:31:16 +08:00
use Kiri\Router\Base\Middleware;
2023-04-15 23:29:27 +08:00
/**
*
*/
class RouterCollector implements \ArrayAccess, \IteratorAggregate
{
2023-10-17 20:19:38 +08:00
/**
* @var array
*/
2023-06-12 15:31:46 +08:00
private array $_item = [];
2023-04-15 23:29:27 +08:00
2023-10-17 20:19:38 +08:00
/**
* @var array
*/
2023-06-12 15:31:46 +08:00
private array $dump = [];
2023-04-19 23:11:54 +08:00
2023-10-17 20:19:38 +08:00
/**
* @var array
*/
2023-06-12 15:31:46 +08:00
public array $groupTack = [];
2023-04-15 23:29:27 +08:00
2023-06-12 15:31:46 +08:00
/**
2023-06-28 15:37:32 +08:00
* @var array<string, Handler>
2023-06-12 15:31:46 +08:00
*/
2023-06-28 15:37:32 +08:00
private array $methods = [];
2023-06-12 15:31:46 +08:00
2023-06-27 16:29:09 +08:00
2023-10-17 20:39:08 +08:00
/**
* @var array<string, HttpRequestHandler>
*/
protected array $httpHandler = [];
2023-08-25 00:26:53 +08:00
/**
* @var Handler
*/
protected Handler $found;
2023-10-18 10:37:41 +08:00
/**
* @var ControllerInterpreter
*/
#[Container(ControllerInterpreter::class)]
public ControllerInterpreter $interpreter;
/**
* @var ContainerInterface
*/
#[Container(ContainerInterface::class)]
public ContainerInterface $container;
2023-08-25 00:26:53 +08:00
/**
2023-12-03 01:26:55 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2023-08-25 00:26:53 +08:00
* @throws ReflectionException
*/
public function __construct()
{
2023-09-15 09:13:59 +08:00
$found = di(NotFoundController::class);
2023-12-03 01:26:55 +08:00
$reflection = new ReflectionMethod($found, 'fail');
2023-09-15 09:13:59 +08:00
$this->found = new Handler([$found, 'fail'], [], $reflection->getReturnType());
2023-08-25 00:26:53 +08:00
}
2023-10-17 17:18:56 +08:00
/**
* @return Handler[]
*/
public function getMethods(): array
{
return $this->methods;
}
2023-10-17 20:13:29 +08:00
/**
* @param string $method
* @param HttpRequestHandler $handler
* @return void
*/
2023-10-17 20:39:08 +08:00
public function setHttpHandler(string $method, HttpRequestHandler $handler): void
2023-10-17 20:13:29 +08:00
{
2023-10-17 20:39:08 +08:00
$this->httpHandler[$method] = $handler;
2023-10-17 20:13:29 +08:00
}
2023-06-12 15:31:46 +08:00
/**
* @return array
*/
public function getDump(): array
{
return $this->dump;
}
/**
* @return Traversable
*/
public function getIterator(): Traversable
{
return new \ArrayIterator($this->_item);
}
2023-04-15 23:29:27 +08:00
2023-06-12 15:31:46 +08:00
/**
* @param array $method
* @param string $route
* @param string|array|Closure $closure
*/
public function addRoute(array $method, string $route, string|array|Closure $closure): void
{
try {
2023-11-17 00:02:41 +08:00
$route = $this->_splicing_routing($route);
2023-06-12 15:31:46 +08:00
if ($closure instanceof Closure) {
2023-10-18 10:37:41 +08:00
$handler = $this->interpreter->addRouteByClosure($closure);
2023-06-12 15:31:46 +08:00
} else {
2023-10-18 10:37:41 +08:00
$handler = $this->resolve($closure, $this->interpreter);
2023-06-12 15:31:46 +08:00
}
foreach ($method as $value) {
if ($value instanceof RequestMethod) {
$value = $value->getString();
}
2023-11-22 10:35:50 +08:00
$handler->setRequestMethod($value);
2023-06-12 15:31:46 +08:00
if (is_array($closure)) {
$closure[0] = is_object($closure[0]) ? get_class($closure[0]) : $closure;
} else if (is_string($closure)) {
$closure = explode('@', $closure);
}
$this->dump[] = [
'method' => $value,
'path' => $route,
'callback' => $closure instanceof Closure ? 'Closure' : $closure
];
$this->register($route, $value, $handler);
}
} catch (Throwable $throwable) {
error($throwable);
}
}
/**
* @param string|array $closure
* @param ControllerInterpreter $interpreter
* @return Handler
* @throws ReflectionException
2023-10-18 10:37:41 +08:00
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
2023-06-12 15:31:46 +08:00
*/
private function resolve(string|array $closure, ControllerInterpreter $interpreter): Handler
{
if (is_array($closure)) {
2023-11-17 00:02:41 +08:00
return $interpreter->addRouteByString(... $closure);
}
if (!str_contains($closure, '@')) {
$closure .= '@';
2023-06-12 15:31:46 +08:00
}
2023-11-17 00:02:41 +08:00
[$className, $method] = explode('@', $closure);
$class = $this->container->get($this->resetName($className));
2023-06-12 15:31:46 +08:00
return $interpreter->addRouteByString($class, $method);
}
/**
* @param string $className
* @return string
*/
private function resetName(string $className): string
{
$namespace = array_filter(array_column($this->groupTack, 'namespace'));
if (count($namespace) < 1) {
return $className;
}
return implode('\\', $namespace) . '\\' . $className;
}
/**
* @param string $path
* @param string $method
* @param Handler $handler
* @return void
* @throws Exception
*/
public function register(string $path, string $method, Handler $handler): void
{
2023-06-28 15:21:01 +08:00
$this->methods[$method . '_' . $path] = $handler;
2023-06-12 15:31:46 +08:00
$this->registerMiddleware($handler->getClass(), $handler->getMethod());
}
/**
* @param string $class
* @param string $method
* @return void
* @throws Exception
*/
public function registerMiddleware(string $class, string $method): void
{
$middlewares = \request()->middlewares;
if (count($middlewares) > 0) {
$this->appendMiddleware($middlewares, $class, $method);
}
$middlewares = array_column($this->groupTack, 'middleware');
if (count($middlewares) > 0) {
$this->appendMiddleware($middlewares, $class, $method);
}
}
/**
* @param array $middlewares
* @param $class
* @param $method
* @return void
* @throws
*/
private function appendMiddleware(array $middlewares, $class, $method): void
{
2023-10-18 10:37:41 +08:00
$manager = $this->container->get(Middleware::class);
2023-06-12 15:31:46 +08:00
foreach ($middlewares as $middleware) {
if (is_string($middleware)) {
$middleware = [$middleware];
}
foreach ($middleware as $value) {
$manager->set($class, $method, $value);
}
}
}
/**
* @param string $path
* @param string $method
2023-10-17 20:13:29 +08:00
* @return HttpRequestHandler
2023-10-17 21:10:49 +08:00
* @throws ReflectionException
2023-06-12 15:31:46 +08:00
*/
2023-10-17 20:13:29 +08:00
public function query(string $path, string $method): HttpRequestHandler
2023-06-12 15:31:46 +08:00
{
2023-10-17 20:39:08 +08:00
return $this->httpHandler[$method . '_' . $path] ?? new HttpRequestHandler([], $this->found);
2023-06-12 15:31:46 +08:00
}
2023-04-19 23:11:54 +08:00
2023-06-12 15:31:46 +08:00
/**
* @param string $route
* @return string
*/
protected function _splicing_routing(string $route): string
{
2023-09-15 09:13:59 +08:00
$route = ltrim($route, '/');
2023-06-12 15:31:46 +08:00
$prefix = array_column($this->groupTack, 'prefix');
if (empty($prefix = array_filter($prefix))) {
return '/' . $route;
}
return '/' . implode('/', $prefix) . '/' . $route;
}
/**
* @param mixed $offset
* @return bool
*/
public function offsetExists(mixed $offset): bool
{
// TODO: Implement offsetExists() method.
return isset($this->_item[$offset]);
}
2023-04-15 23:29:27 +08:00
2023-06-12 15:31:46 +08:00
/**
* @param mixed $offset
* @return Router|null
2023-05-26 11:23:21 +08:00
*/
2023-06-12 15:31:46 +08:00
public function offsetGet(mixed $offset): ?Router
{
if ($this->offsetExists($offset)) {
return $this->_item[$offset];
}
return null;
}
/**
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet(mixed $offset, mixed $value): void
{
// TODO: Implement offsetSet() method.
$this->_item[$offset] = $value;
}
/**
* @param mixed $offset
* @return void
2023-05-26 11:38:36 +08:00
*/
2023-06-12 15:31:46 +08:00
public function offsetUnset(mixed $offset): void
{
unset($this->_item[$offset]);
}
2023-04-15 23:29:27 +08:00
}