*/ private array $methods = []; /** * @var array */ protected array $httpHandler = []; /** * @var Handler */ protected Handler $found; /** * @throws */ public function __construct() { $this->found = new Handler([NotFoundController::class, 'fail'], [], ResponseFormat::class); } /** * @return Handler[] */ public function getMethods(): array { return $this->methods; } /** * @param string $method * @param HttpRequestHandler $handler * @return void */ public function setHttpHandler(string $method, HttpRequestHandler $handler): void { $this->httpHandler[$method] = $handler; } /** * @return array */ public function getDump(): array { return $this->dump; } /** * @return Traversable */ public function getIterator(): Traversable { return new \ArrayIterator($this->_item); } /** * @param array $method * @param string $route * @param string|array|Closure $closure */ public function addRoute(array $method, string $route, string|array|Closure $closure): void { try { $route = $this->_splicing_routing($route); if ($closure instanceof Closure) { $handler = di(ControllerInterpreter::class)->addRouteByClosure($closure); } else { $handler = $this->resolve($closure, di(ControllerInterpreter::class)); } foreach ($method as $value) { if ($value instanceof RequestMethod) { $value = $value->getString(); } $handler->setRequestMethod($value); if (is_array($closure)) { $closure[0] = is_object($closure[0]) ? get_class($closure[0]) : $closure; } else if (is_string($closure)) { $closure = explode('@', $closure); } $this->register($route, $value, $handler); } } catch (Throwable $throwable) { error($throwable); } } /** * @return array */ public function dump(): array { $array = []; foreach ($this->methods as $methodPath => $handler) { [$path, $method] = explode('_', $methodPath); $controller = $handler instanceof Closure ? '\Closure' : $handler->getClass() . '::' . $handler->getMethod(); $array[] = [ 'path' => $path, 'method' => $method, 'handler' => $controller ]; } return $array; } /** * @param string|array $closure * @param ControllerInterpreter $interpreter * @return Handler * @throws */ private function resolve(string|array $closure, ControllerInterpreter $interpreter): Handler { $container = \Kiri::getDi(); if (is_array($closure)) { if (is_string($closure[0])) { $closure[0] = $container->get($closure[0]); } return $interpreter->addRouteByString(... $closure); } if (!str_contains($closure, '@')) { $closure .= '@'; } [$className, $method] = explode('@', $closure); $class = $container->get($this->resetName($className)); 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 */ public function register(string $path, string $method, Handler $handler): void { $this->methods[$method . '_' . $path] = $handler; $handler->setMiddlewares($this->registerMiddleware($handler->getClass(), $handler->getMethod())); } /** * @param string $class * @param string $method * @return array * @throws ReflectionException */ public function registerMiddleware(string $class, string $method): array { $response = []; $middlewares = \config('servers.request.middlewares', []); if (is_array($middlewares) && count($middlewares) > 0) { $response = $this->appendMiddleware($response, $middlewares); } $middlewares = array_column($this->groupTack, 'middleware'); $response = $this->appendMiddleware($response, $middlewares); return $this->read_method_middleware($response, $class, $method); } /** * @param array $response * @param string $class * @param string $method * @return array * @throws ReflectionException */ private function read_method_middleware(array $response, string $class, string $method): array { $reflect = \Kiri::getDi()->getReflectionClass($class); $attributes = $reflect->getMethod($method)->getAttributes(Annotate\Middleware::class); foreach ($attributes as $attribute) { /** @var Annotate\Middleware $instance */ $instance = $attribute->newInstance(); $data = $instance->middleware; if (is_string($data)) { $data = [$data]; } foreach ($data as $middleware) { if (!in_array($middleware, $response)) { $response[] = $middleware; } } } return $response; } /** * @param array $response * @param array $middlewares * @return array */ private function appendMiddleware(array $response, array $middlewares): array { foreach ($middlewares as $middleware) { if (is_string($middleware)) { $middleware = [$middleware]; } foreach ($middleware as $value) { if (!in_array($value, $response)) { $response[] = $value; } } } return $response; } /** * @param string $path * @param string $method * @return HttpRequestHandler * @throws */ public function query(string $path, string $method): HttpRequestHandler { return $this->httpHandler[$method . '_' . $path] ?? new HttpRequestHandler([], $this->found); } /** * @param string $route * @return string */ protected function _splicing_routing(string $route): string { $route = ltrim($route, '/'); $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]); } /** * @param mixed $offset * @return Router|null */ 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 */ public function offsetUnset(mixed $offset): void { unset($this->_item[$offset]); } }