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-04-15 23:29:27 +08:00
|
|
|
use ReflectionException;
|
|
|
|
|
use Throwable;
|
|
|
|
|
use Traversable;
|
2023-04-15 23:31:16 +08:00
|
|
|
use Kiri\Router\Base\Middleware;
|
|
|
|
|
use Kiri\Core\HashMap;
|
2023-04-15 23:29:27 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
class RouterCollector implements \ArrayAccess, \IteratorAggregate
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private array $_item = [];
|
|
|
|
|
|
|
|
|
|
|
2023-04-19 23:11:54 +08:00
|
|
|
private array $dump = [];
|
|
|
|
|
|
|
|
|
|
|
2023-04-15 23:29:27 +08:00
|
|
|
public array $groupTack = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-15 23:31:16 +08:00
|
|
|
* @var HashMap
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
private HashMap $methods;
|
2023-04-15 23:29:27 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
2023-04-15 23:31:16 +08:00
|
|
|
$this->methods = new HashMap();
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-19 23:11:54 +08:00
|
|
|
/**
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public function getDump(): array
|
|
|
|
|
{
|
|
|
|
|
return $this->dump;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-15 23:29:27 +08:00
|
|
|
/**
|
|
|
|
|
* @return Traversable
|
|
|
|
|
*/
|
|
|
|
|
public function getIterator(): Traversable
|
|
|
|
|
{
|
|
|
|
|
return new \ArrayIterator($this->_item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-16 17:23:37 +08:00
|
|
|
* @param array $method
|
2023-04-15 23:29:27 +08:00
|
|
|
* @param string $route
|
2023-04-16 17:23:37 +08:00
|
|
|
* @param string|array|Closure $closure
|
2023-04-19 10:51:08 +08:00
|
|
|
* @throws ReflectionException
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-19 10:51:08 +08:00
|
|
|
public function addRoute(array $method, string $route, string|array|Closure $closure): void
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
|
|
|
|
try {
|
|
|
|
|
$route = $this->_splicing_routing($route);
|
2023-04-15 23:31:16 +08:00
|
|
|
$interpreter = Kiri::getDi()->get(ControllerInterpreter::class);
|
2023-04-15 23:29:27 +08:00
|
|
|
if ($closure instanceof Closure) {
|
2023-04-15 23:31:16 +08:00
|
|
|
$handler = $interpreter->addRouteByClosure($closure);
|
|
|
|
|
} else {
|
|
|
|
|
$handler = $this->resolve($closure, $interpreter);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
foreach ($method as $value) {
|
2023-04-15 23:31:16 +08:00
|
|
|
if ($value instanceof RequestMethod) {
|
|
|
|
|
$value = $value->getString();
|
|
|
|
|
}
|
2023-04-19 23:11:54 +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);
|
|
|
|
|
}
|
2023-04-19 23:19:18 +08:00
|
|
|
$this->dump[] = [
|
|
|
|
|
'method' => $value,
|
|
|
|
|
'path' => $route,
|
|
|
|
|
'callback' => $closure instanceof Closure ? 'Closure' : $closure
|
|
|
|
|
];
|
2023-04-15 23:31:16 +08:00
|
|
|
$this->register($route, $value, $handler);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
} catch (Throwable $throwable) {
|
2023-04-18 23:47:30 +08:00
|
|
|
error($throwable);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-16 17:23:37 +08:00
|
|
|
* @param string|array $closure
|
2023-04-15 23:31:16 +08:00
|
|
|
* @param ControllerInterpreter $interpreter
|
|
|
|
|
* @return Handler
|
2023-04-15 23:29:27 +08:00
|
|
|
* @throws ReflectionException
|
|
|
|
|
*/
|
2023-04-16 17:23:37 +08:00
|
|
|
private function resolve(string|array $closure, ControllerInterpreter $interpreter): Handler
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-16 17:23:37 +08:00
|
|
|
if (is_array($closure)) {
|
|
|
|
|
[$class, $method] = $closure;
|
|
|
|
|
} else {
|
2023-04-24 21:41:09 +08:00
|
|
|
if (!str_contains($closure,'@')) {
|
|
|
|
|
$closure .= '@';
|
|
|
|
|
}
|
2023-04-16 17:23:37 +08:00
|
|
|
[$className, $method] = explode('@', $closure);
|
2023-04-15 23:29:27 +08:00
|
|
|
|
2023-04-16 17:23:37 +08:00
|
|
|
$class = Kiri::getDi()->get($this->resetName($className));
|
|
|
|
|
}
|
2023-04-15 23:31:16 +08:00
|
|
|
return $interpreter->addRouteByString($class, $method);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-15 23:31:16 +08:00
|
|
|
* @param string $className
|
|
|
|
|
* @return string
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
private function resetName(string $className): string
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-15 23:31:16 +08:00
|
|
|
$namespace = array_filter(array_column($this->groupTack, 'namespace'));
|
|
|
|
|
if (count($namespace) < 1) {
|
|
|
|
|
return $className;
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
2023-04-15 23:31:16 +08:00
|
|
|
return implode('\\', $namespace) . '\\' . $className;
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param string $path
|
2023-04-15 23:31:16 +08:00
|
|
|
* @param string $method
|
|
|
|
|
* @param Handler $handler
|
|
|
|
|
* @return void
|
2023-04-15 23:29:27 +08:00
|
|
|
* @throws ReflectionException
|
|
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
public function register(string $path, string $method, Handler $handler): void
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-15 23:31:16 +08:00
|
|
|
$hashMap = HashMap::Tree($this->methods, $method);
|
|
|
|
|
foreach (str_split($path, 4) as $item) {
|
|
|
|
|
if ($hashMap->has($item)) {
|
|
|
|
|
$hashMap = $hashMap->get($item);
|
|
|
|
|
} else {
|
2023-04-16 03:12:31 +08:00
|
|
|
$hashMap->put($item, $hashMap = new HashMap());
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
}
|
2023-04-15 23:31:16 +08:00
|
|
|
$hashMap->put('handler', $handler);
|
2023-04-16 12:44:43 +08:00
|
|
|
$this->registerMiddleware($handler->getClass(), $handler->getMethod());
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-16 12:44:43 +08:00
|
|
|
* @param string $class
|
|
|
|
|
* @param string $method
|
2023-04-15 23:29:27 +08:00
|
|
|
* @return void
|
|
|
|
|
* @throws ReflectionException
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2023-04-16 12:44:43 +08:00
|
|
|
public function registerMiddleware(string $class, string $method): void
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-19 22:13:21 +08:00
|
|
|
$middlewares = Kiri::service()->get('request')->middlewares;
|
2023-04-15 23:31:16 +08:00
|
|
|
if (count($middlewares) > 0) {
|
2023-04-21 17:40:40 +08:00
|
|
|
$this->appendMiddleware($middlewares, $class, $method);
|
|
|
|
|
}
|
|
|
|
|
$middlewares = array_column($this->groupTack, 'middleware');
|
|
|
|
|
if (count($middlewares) > 0) {
|
|
|
|
|
$this->appendMiddleware($middlewares, $class, $method);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-04-21 17:40:40 +08:00
|
|
|
/**
|
|
|
|
|
* @param array $middlewares
|
|
|
|
|
* @param $class
|
|
|
|
|
* @param $method
|
|
|
|
|
* @return void
|
|
|
|
|
* @throws
|
|
|
|
|
*/
|
|
|
|
|
private function appendMiddleware(array $middlewares, $class, $method): void
|
|
|
|
|
{
|
|
|
|
|
$manager = Kiri::getDi()->get(Middleware::class);
|
|
|
|
|
foreach ($middlewares as $middleware) {
|
|
|
|
|
if (is_string($middleware)) {
|
|
|
|
|
$middleware = [$middleware];
|
|
|
|
|
}
|
|
|
|
|
foreach ($middleware as $value) {
|
|
|
|
|
$manager->set($class, $method, $value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-15 23:29:27 +08:00
|
|
|
/**
|
|
|
|
|
* @param string $path
|
|
|
|
|
* @param string $method
|
2023-04-15 23:31:16 +08:00
|
|
|
* @return Handler|null
|
2023-04-16 12:44:43 +08:00
|
|
|
* @throws ReflectionException
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
public function query(string $path, string $method): ?Handler
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-15 23:31:16 +08:00
|
|
|
if (!$this->methods->has($method)) {
|
|
|
|
|
return $this->NotFundHandler($path);
|
|
|
|
|
}
|
|
|
|
|
$parent = $this->methods->get($method);
|
|
|
|
|
foreach (str_split($path, 4) as $item) {
|
|
|
|
|
$parent = $parent->get($item);
|
|
|
|
|
if ($parent === null) {
|
|
|
|
|
return $this->NotFundHandler($path);
|
|
|
|
|
}
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
2023-04-15 23:31:16 +08:00
|
|
|
return $parent->get('handler');
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-15 23:31:16 +08:00
|
|
|
* @param string $path
|
|
|
|
|
* @return Handler
|
2023-04-16 02:58:01 +08:00
|
|
|
* @throws ReflectionException
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
private function NotFundHandler(string $path): Handler
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-16 02:58:01 +08:00
|
|
|
return new Handler([di(NotFoundController::class), 'fail'], []);
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2023-04-15 23:31:16 +08:00
|
|
|
* @param string $route
|
|
|
|
|
* @return string
|
2023-04-15 23:29:27 +08:00
|
|
|
*/
|
2023-04-15 23:31:16 +08:00
|
|
|
protected function _splicing_routing(string $route): string
|
2023-04-15 23:29:27 +08:00
|
|
|
{
|
2023-04-15 23:31:16 +08:00
|
|
|
$route = ltrim($route, '/');
|
|
|
|
|
$prefix = array_column($this->groupTack, 'prefix');
|
|
|
|
|
if (empty($prefix = array_filter($prefix))) {
|
|
|
|
|
return '/' . $route;
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
2023-04-15 23:31:16 +08:00
|
|
|
return '/' . implode('/', $prefix) . '/' . $route;
|
2023-04-15 23:29:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @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]);
|
|
|
|
|
}
|
|
|
|
|
}
|