diff --git a/http-helper/Route/Node.php b/http-helper/Route/Node.php index e0fb3274..dc92cc52 100644 --- a/http-helper/Route/Node.php +++ b/http-helper/Route/Node.php @@ -69,10 +69,9 @@ class Node } - /** - * @throws NotFindClassException - * @throws ReflectionException - */ + /** + * @param \Http\Route\Router $router + */ public function __construct(public Router $router) { $eventDispatcher = di(EventProvider::class); @@ -197,7 +196,7 @@ class Node * @param RequestInterface $request * @return bool */ - #[Pure] public function methodAllow(RequestInterface $request): bool + public function methodAllow(RequestInterface $request): bool { if (!in_array($request->getMethod(), $this->method)) { return true; diff --git a/http-helper/Route/Router.php b/http-helper/Route/Router.php index 95547893..be2303fe 100644 --- a/http-helper/Route/Router.php +++ b/http-helper/Route/Router.php @@ -15,6 +15,7 @@ use Kiri\Abstracts\Config; use Kiri\Exception\ConfigException; use Kiri\Exception\NotFindClassException; use Kiri\Kiri; +use Psr\Http\Message\UriInterface; use ReflectionException; use Server\Constrict\RequestInterface; use Throwable; @@ -28,535 +29,545 @@ defined('ROUTER_HASH') or define('ROUTER_HASH', 2); */ class Router extends HttpService implements RouterInterface { - /** @var Node[] $nodes */ - public array $nodes = []; - public array $groupTacks = []; - public ?string $namespace = 'App\\Http\\Controllers'; - - /** @var string[] */ - public array $methods = ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'RECEIVE', 'HEAD']; - - - public ?Closure $middleware = null; - - public int $useTree = ROUTER_TREE; - - - /** - * @var RequestInterface - */ - #[Inject(RequestInterface::class)] - public RequestInterface $request; - - - /** - * @param Closure $middleware - */ - public function setMiddleware(Closure $middleware): void - { - $this->middleware = $middleware; - } - - - /** - * @throws ConfigException - * @throws Exception - * 初始化函数路径 - */ - public function init() - { - $this->namespace = Config::get('http.namespace', $this->namespace); - } - - - /** - * @return mixed - * @throws ConfigException - * @throws Exception - */ - public static function getNamespace(): string - { - $router = Kiri::getDi()->get(Router::class); - - return Config::get('http.namespace', $router->namespace); - } - - - /** - * @param bool $useTree - */ - public function setUseTree(bool $useTree): void - { - $this->useTree = $useTree ? ROUTER_TREE : ROUTER_HASH; - } - - - /** - * @param $path - * @param $handler - * @param string $method - * @return ?Node - * @throws - */ - public function addRoute($path, $handler, string $method = 'any'): ?Node - { - if ($handler instanceof Closure) { - $handler = Closure::bind($handler, di(Controller::class)); - } - return $this->tree($path, $handler, $method); - } - - /** - * @param $path - * @return string - */ - #[Pure] private function resolve($path): string - { - $paths = array_column($this->groupTacks, 'prefix'); - if (empty($paths)) { - return '/' . ltrim($path, '/'); - } - $paths = '/' . implode('/', $paths); - if ($path != '/') { - return $paths . '/' . ltrim($path, '/'); - } - return $paths . '/'; - } - - - /** - * @param $path - * @param $handler - * @param string $method - * @return Node - * @throws Exception - */ - private function tree($path, $handler, string $method = 'any'): Node - { - $explode = $this->split($path); - $start = array_shift($explode); - $parent = $this->nodes[$start] ?? null; - if (is_null($parent)) { - $parent = $this->nodes[$start] = $this->NodeInstance($start, 0, $method); - } - if (!empty($explode)) { - $parent = $this->bindNode($parent, $explode, $method); - } - if (!in_array($method, $parent->method)) { - $parent->method[] = $method; - } - return $parent->setHandler($handler, $method, $path); - } - - - /** - * @param Node $parent - * @param array $explode - * @param $method - * @return Node - * @throws Exception - */ - private function bindNode(Node $parent, array $explode, $method): Node - { - $a = 0; - foreach ($explode as $value) { - ++$a; - $search = $parent->findNode($value); - if ($search === null) { - $parent = $parent->addChild($this->NodeInstance($value, $a, $method)); - } else { - $parent = $search; - } - } - return $parent; - } - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function socket($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'SOCKET'); - } - - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function post($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'POST'); - } - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function get($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'GET'); - } - - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function options($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'OPTIONS'); - } - - - /** - * @param $route - * @param $handler - * @throws - */ - public static function any($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - foreach ($router->methods as $method) { - $router->addRoute($route, $handler, $method); - } - } - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function delete($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'DELETE'); - } - - - /** - * @param $route - * @param $handler - * @return void - * @throws Exception - */ - public static function head($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'HEAD'); - } - - - /** - * @param $route - * @param $handler - * @return void - * @throws - */ - public static function put($route, $handler): void - { - $router = Kiri::getDi()->get(Router::class); - $router->addRoute($route, $handler, 'PUT'); - } - - /** - * @param $value - * @param int $index - * @param string $method - * @return Node - * @throws - */ - public function NodeInstance($value, int $index = 0, string $method = 'GET'): Node - { - $node = new Node($this); - $node->childes = []; - $node->path = $value; - $node->index = $index; - $node->method[] = $method; - $node->namespace = $this->loadNamespace(); - - $name = array_column($this->groupTacks, 'middleware'); - if (is_array($name)) { - $node->addMiddleware($method, $this->resolve_middleware($name)); - } - return $node; - } - - - /** - * @return Closure|null - */ - public function getMiddleware(): ?Closure - { - return $this->middleware; - } - - - /** - * @param string|array $middleware - * @return array - * @throws NotFindClassException - * @throws ReflectionException - */ - private function resolve_middleware(string|array $middleware): array - { - if (is_string($middleware)) { - $middleware = [$middleware]; - } - - $array = []; - foreach ($middleware as $value) { - if (is_array($value)) { - foreach ($value as $item) { - $array[] = $this->getMiddlewareInstance($item); - } - } else { - $array[] = $this->getMiddlewareInstance($value); - } - } - return array_filter($array); - } - - - /** - * @param $value - * @return Closure|array|null - * @throws NotFindClassException - * @throws ReflectionException - * @throws Exception - */ - private function getMiddlewareInstance($value): null|Closure|array - { - if (!is_string($value)) { - return $value; - } - $value = Kiri::createObject($value); - if (!($value instanceof MiddlewareInterface)) { - return null; - } - return [$value, 'onHandler']; - } - - - /** - * @return array - */ - private function loadNamespace(): array - { - $name = array_column($this->groupTacks, 'namespace'); - array_unshift($name, $this->namespace); - return array_filter($name); - } - - /** - * @param array $config - * @param callable $callback - * 路由分组 - */ - public static function group(array $config, callable $callback) - { - $router = Kiri::getDi()->get(Router::class); - $router->groupTacks[] = $config; - - call_user_func($callback); - - array_pop($router->groupTacks); - } - - /** - * @return string - */ - public function addPrefix(): string - { - $prefix = array_column($this->groupTacks, 'prefix'); - - $prefix = array_filter($prefix); - - if (empty($prefix)) { - return ''; - } - - return '/' . implode('/', $prefix); - } - - /** - * @param array|null $explode - * @return Node|null - * 查找指定路由 - * @throws Exception - */ - public function tree_search(?array $explode): ?Node - { - $parent = $this->nodes[array_shift($explode)] ?? null; - if (is_null($parent)) { - return null; - } - while ($value = array_shift($explode)) { - $node = $parent->findNode($value); - if (!$node) { - return null; - } - $parent = $node; - } - return $parent; - } - - /** - * @param $path - * @return array|null - */ - public function split($path): ?array - { - $path = $this->addPrefix() . '/' . ltrim($path, '/'); - if ($path === '/') { - return ['/']; - } - $filter = array_filter(explode('/', $path)); - if (!empty($filter)) { - return $filter; - } - return ['/']; - } - - /** - * @return array - */ - public function each(): array - { - $paths = []; - foreach ($this->nodes as $_node) { - /** @var Node[] $node */ - $paths[] = ['method' => $_node->method, 'path' => $_node->sourcePath, 'alias' => $_node->getAlias()]; - $paths = $this->getChildes($_node, $paths); - } - return $paths; - } - - - /** - * @param Node $node - * @param array $path - * @return array - */ - private function getChildes(Node $node, array $path): array - { - foreach ($node->childes as $item) { - $path[] = ['method' => $item->method, 'path' => $item->sourcePath, 'alias' => $item->getAlias()]; - if (!empty($item->childes)) { - $path = $this->getChildes($item, $path); - } - } - return $path; - } - - - /** - * @param $exception - * @return mixed - * @throws Exception - */ - public function exception($exception): mixed - { - return Kiri::app()->getLogger()->exception($exception); - } - - - /** - * @param $uri - * @param $method - * @return Node|null - */ - public function search($uri, $method): Node|null - { - if (!isset($this->nodes[$method])) { - return null; - } - $methods = $this->nodes[$method]; - if (isset($methods[$uri])) { - return $methods[$uri]; - } - return $methods['/'] ?? null; - } - - - /** - * @param RequestInterface $request - * @return Node|null - * 树杈搜索 - * @throws Exception - */ - public function Branch_search(RequestInterface $request): ?Node - { - $uri = $request->getUri(); - if ($request->isMethod('OPTIONS')) { - $node = $this->tree_search(['*']); - } - if (!isset($node)) { - $_explode = array_filter(explode('/', $uri->getPath())); - if (empty($_explode)) { - $_explode = ['/']; - } - $node = $this->tree_search($_explode); - } - return $node; - } - - - /** - * @throws - */ - public function _loader() - { - $this->loadRouteDir(APP_PATH . 'routes'); - } - - /** - * @param $path - * @throws Exception - * 加载目录下的路由文件 - */ - private function loadRouteDir($path) - { - $files = glob($path . '/*'); - for ($i = 0; $i < count($files); $i++) { - if (is_dir($files[$i])) { - $this->loadRouteDir($files[$i]); - } else { - $this->loadRouterFile($files[$i]); - } - } - } - - - /** - * @param $files - * @throws Exception - */ - private function loadRouterFile($files) - { - try { - include_once "$files"; - } catch (Throwable $exception) { - $this->addError($exception, 'throwable'); - } finally { - if (isset($exception)) { - unset($exception); - } - } - } + /** @var \Http\Route\Node[] $nodes */ + public array $nodes = []; + public array $groupTacks = []; + public ?string $namespace = 'App\\Http\\Controllers'; + + /** @var string[] */ + public array $methods = ['GET', 'POST', 'OPTIONS', 'PUT', 'DELETE', 'RECEIVE', 'HEAD']; + + + public ?Closure $middleware = null; + + public int $useTree = ROUTER_TREE; + + + /** + * @var RequestInterface + */ + #[Inject(RequestInterface::class)] + public RequestInterface $request; + + + /** + * @param Closure $middleware + */ + public function setMiddleware(Closure $middleware): void + { + $this->middleware = $middleware; + } + + + /** + * @throws ConfigException + * @throws Exception + * 初始化函数路径 + */ + public function init() + { + $this->namespace = Config::get('http.namespace', $this->namespace); + } + + + /** + * @return mixed + * @throws ConfigException + * @throws Exception + */ + public static function getNamespace(): string + { + $router = Kiri::getDi()->get(Router::class); + + return Config::get('http.namespace', $router->namespace); + } + + + /** + * @param bool $useTree + */ + public function setUseTree(bool $useTree): void + { + $this->useTree = $useTree ? ROUTER_TREE : ROUTER_HASH; + } + + + /** + * @param $path + * @param $handler + * @param string $method + * @return ?Node + * @throws + */ + public function addRoute($path, $handler, string $method = 'any'): ?Node + { + if ($handler instanceof Closure) { + $handler = Closure::bind($handler, di(Controller::class)); + } + return $this->tree($path, $handler, $method); + } + + /** + * @param $path + * @return string + */ + #[Pure] private function resolve($path): string + { + $paths = array_column($this->groupTacks, 'prefix'); + if (empty($paths)) { + return '/' . ltrim($path, '/'); + } + $paths = '/' . implode('/', $paths); + if ($path != '/') { + return $paths . '/' . ltrim($path, '/'); + } + return $paths . '/'; + } + + + /** + * @param $path + * @param $handler + * @param string $method + * @return Node + * @throws Exception + */ + private function tree($path, $handler, string $method = 'any'): Node + { + [$parent, $explode] = $this->getRootNode($path, $method); + + if (!empty($explode)) { + $parent = $this->bindNode($parent, $explode, $method); + } + + if (!in_array($method, $parent->method)) { + $parent->method[] = $method; + } + return $parent->setHandler($handler, $method, $path); + } + + + /** + * @param $root + * @param $method + * @param bool $create + * @return array<\Http\Route\Node, array> + */ + private function getRootNode($root, $method, bool $create = true): array + { + [$start, $explode] = $this->path_recombination($root); + + foreach ($this->nodes as $node) { + if ($node->path == $start) { + return [$node, $explode]; + } + } + if ($create) { + $parent = $this->NodeInstance($root, 0, $method); + $this->nodes[] = $parent; + + return [$parent, $explode]; + } + return [null, null]; + } + + + /** + * @param Node $parent + * @param array $explode + * @param $method + * @return Node + * @throws Exception + */ + private function bindNode(Node $parent, array $explode, $method): Node + { + $a = 0; + foreach ($explode as $value) { + ++$a; + $search = $parent->findNode($value); + if ($search === null) { + $parent = $parent->addChild($this->NodeInstance($value, $a, $method)); + } else { + $parent = $search; + } + } + return $parent; + } + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function socket($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'SOCKET'); + } + + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function post($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'POST'); + } + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function get($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'GET'); + } + + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function options($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'OPTIONS'); + } + + + /** + * @param $route + * @param $handler + * @throws + */ + public static function any($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + foreach ($router->methods as $method) { + $router->addRoute($route, $handler, $method); + } + } + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function delete($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'DELETE'); + } + + + /** + * @param $route + * @param $handler + * @return void + * @throws Exception + */ + public static function head($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'HEAD'); + } + + + /** + * @param $route + * @param $handler + * @return void + * @throws + */ + public static function put($route, $handler): void + { + $router = Kiri::getDi()->get(Router::class); + $router->addRoute($route, $handler, 'PUT'); + } + + /** + * @param $value + * @param int $index + * @param string $method + * @return Node + * @throws + */ + public function NodeInstance($value, int $index = 0, string $method = 'GET'): Node + { + $node = new Node($this); + $node->childes = []; + $node->path = $value; + $node->index = $index; + $node->method[] = $method; + $node->namespace = $this->loadNamespace(); + + $name = array_column($this->groupTacks, 'middleware'); + if (is_array($name)) { + $node->addMiddleware($method, $this->resolve_middleware($name)); + } + return $node; + } + + + /** + * @return Closure|null + */ + public function getMiddleware(): ?Closure + { + return $this->middleware; + } + + + /** + * @param string|array $middleware + * @return array + * @throws NotFindClassException + * @throws ReflectionException + */ + private function resolve_middleware(string|array $middleware): array + { + if (is_string($middleware)) { + $middleware = [$middleware]; + } + + $array = []; + foreach ($middleware as $value) { + if (is_array($value)) { + foreach ($value as $item) { + $array[] = $this->getMiddlewareInstance($item); + } + } else { + $array[] = $this->getMiddlewareInstance($value); + } + } + return array_filter($array); + } + + + /** + * @param $value + * @return Closure|array|null + * @throws NotFindClassException + * @throws ReflectionException + * @throws Exception + */ + private function getMiddlewareInstance($value): null|Closure|array + { + if (!is_string($value)) { + return $value; + } + $value = Kiri::createObject($value); + if (!($value instanceof MiddlewareInterface)) { + return null; + } + return [$value, 'onHandler']; + } + + + /** + * @return array + */ + private function loadNamespace(): array + { + $name = array_column($this->groupTacks, 'namespace'); + array_unshift($name, $this->namespace); + return array_filter($name); + } + + /** + * @param array $config + * @param callable $callback + * 路由分组 + */ + public static function group(array $config, callable $callback) + { + $router = Kiri::getDi()->get(Router::class); + $router->groupTacks[] = $config; + + call_user_func($callback); + + array_pop($router->groupTacks); + } + + /** + * @return string + */ + public function addPrefix(): string + { + $prefix = array_column($this->groupTacks, 'prefix'); + + $prefix = array_filter($prefix); + + if (empty($prefix)) { + return ''; + } + + return '/' . implode('/', $prefix); + } + + /** + * @param array|null $explode + * @return Node|null + * 查找指定路由 + * @throws Exception + */ + public function tree_search(?array $explode): ?Node + { + $start = array_shift($explode); + foreach ($this->nodes as $node) { + if ($node->path == $start) { + $parent = $node; + } + } + if (!isset($parent)) { + return null; + } + while ($value = array_shift($explode)) { + $node = $parent->findNode($value); + if (!$node) { + return null; + } + $parent = $node; + } + return $parent; + } + + /** + * @param $path + * @return array|null + */ + public function path_recombination($path): ?array + { + $path = $this->addPrefix() . '/' . ltrim($path, '/'); + if ($path === '/') { + return ['/', null]; + } + $filter = array_filter(explode('/', $path)); + if (!empty($filter)) { + return [array_shift($filter), $filter]; + } + return ['/', null]; + } + + /** + * @return array + */ + public function each(): array + { + $paths = []; + foreach ($this->nodes as $_node) { + /** @var Node[] $node */ + $paths[] = ['method' => $_node->method, 'path' => $_node->sourcePath, 'alias' => $_node->getAlias()]; + $paths = $this->getChildes($_node, $paths); + } + return $paths; + } + + + /** + * @param Node $node + * @param array $path + * @return array + */ + private function getChildes(Node $node, array $path): array + { + foreach ($node->childes as $item) { + $path[] = ['method' => $item->method, 'path' => $item->sourcePath, 'alias' => $item->getAlias()]; + if (!empty($item->childes)) { + $path = $this->getChildes($item, $path); + } + } + return $path; + } + + + /** + * @param $exception + * @return mixed + * @throws Exception + */ + public function exception($exception): mixed + { + return Kiri::app()->getLogger()->exception($exception); + } + + + /** + * @param RequestInterface $request + * @return Node|null + * 树杈搜索 + * @throws Exception + */ + public function radix_tree(RequestInterface $request): ?Node + { + [$parent, $explode] = $this->getRootNode($request->getUri()->getPath(), + $request->getMethod(), false); + if ($parent && !empty($explode)) { + /** @var \Http\Route\Node $parent */ + foreach ($explode as $value) { + $node = $parent->findNode($value); + if (!$node) { + return null; + } + $parent = $node; + } + } + return $parent; + } + + + /** + * @throws + */ + public function _loader() + { + $this->loadRouteDir(APP_PATH . 'routes'); + } + + /** + * @param $path + * @throws Exception + * 加载目录下的路由文件 + */ + private function loadRouteDir($path) + { + $files = glob($path . '/*'); + for ($i = 0; $i < count($files); $i++) { + if (is_dir($files[$i])) { + $this->loadRouteDir($files[$i]); + } else { + $this->loadRouterFile($files[$i]); + } + } + } + + + /** + * @param $files + * @throws Exception + */ + private function loadRouterFile($files) + { + try { + include_once "$files"; + } catch (Throwable $exception) { + $this->addError($exception, 'throwable'); + } finally { + if (isset($exception)) { + unset($exception); + } + } + } } diff --git a/http-message/Message.php b/http-message/Message.php index a36693aa..45be58a4 100644 --- a/http-message/Message.php +++ b/http-message/Message.php @@ -3,7 +3,7 @@ namespace Protocol\Message; use JetBrains\PhpStorm\Pure; -use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; @@ -13,236 +13,283 @@ use Psr\Http\Message\StreamInterface; trait Message { - /** - * @var string - */ - protected string $version; + /** + * @var string + */ + protected string $version; - /** - * @var StreamInterface - */ - protected StreamInterface $stream; + /** + * @var StreamInterface + */ + protected StreamInterface $stream; - /** - * @var array - */ - protected array $headers = []; + /** + * @var array + */ + protected array $headers = []; - - /** - * @var array|null - */ - protected ?array $cookieParams = []; + /** + * @var array|null + */ + protected ?array $cookieParams = []; - /** - * @return string - */ - public function getProtocolVersion(): string - { - return $this->version; - } + /** + * @return string + */ + public function getProtocolVersion(): string + { + return $this->version; + } - /** - * @param $version - * @return static - */ - public function withProtocolVersion($version): static - { - $this->version = $version; - return $this; - } + /** + * @param $version + * @return static + */ + public function withProtocolVersion($version): static + { + $this->version = $version; + return $this; + } - /** - * @return array - */ - public function getHeaders(): array - { - return $this->headers; - } + /** + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } - /** - * @param $name - * @return bool - */ - public function hasHeader($name): bool - { - return array_key_exists($name, $this->headers); - } + /** + * @param $name + * @return bool + */ + public function hasHeader($name): bool + { + return array_key_exists($name, $this->headers); + } - /** - * @param $name - * @return string|array|null - */ - #[Pure] public function getHeader($name): string|null|array - { - if (!$this->hasHeader($name)) { - return null; - } - return $this->headers[$name]; - } + /** + * @param $name + * @return string|array|null + */ + #[Pure] public function getHeader($name): string|null|array + { + if (!$this->hasHeader($name)) { + return null; + } + return $this->headers[$name]; + } - /** - * @param \Swoole\Http\Request $request - * @return static - */ - public function parseRequestHeaders(\Swoole\Http\Request $request): static - { - $index = strpos($request->getData(), "\r\n\r\n"); - $headers = explode("\r\n", substr($request->getData(), 0, $index)); - array_shift($headers); - foreach ($headers as $header) { - [$key, $value] = explode(': ', $header); - $this->addRequestHeader($key, $value); - } - return $this; - } + /** + * @return array + */ + public function parse_curl_header(): array + { + $_headers = []; + foreach ($this->headers as $key => $val) { + $_headers[] = $key . ': ' . implode(';', $val); + } + return $_headers; + } - /** - * @param $key - * @param $value - */ - private function addRequestHeader($key, $value) - { - $this->headers[$key] = [$value]; - } + /** + * @throws \Exception + */ + public function withData(string $headerString): static + { + [$headers, $body] = explode("\r\n\r\n", $headerString); + + $this->stream = new Stream($body); + + return $this->slip_headers($headers); + } - /** - * @param $name - * @return string|null - */ - #[Pure] public function getHeaderLine($name): string|null - { - if ($this->hasHeader($name)) { - return implode(';', $this->headers[$name]); - } - return null; - } + /** + * @param $headers + * @return $this + * @throws \Exception + */ + private function slip_headers($headers): static + { + $headers = explode("\r\n", $headers); + + $this->resolve_status(array_shift($headers)); + + foreach ($headers as $header) { + [$key, $value] = explode(': ', $header); + $this->withHeader($key, $value); + } + return $this; + } - /** - * @return string|null - */ - #[Pure] public function getContentType(): ?string - { - return $this->getHeaderLine('Content-Type'); - } + /** + * @param string $protocol + */ + private function resolve_status(string $protocol) + { + if ($this instanceof ResponseInterface) { + [$sch, $status, $message] = explode(' ', $protocol); + [$sch, $protocolVersion] = explode('/', $sch); + $this->withProtocolVersion($protocolVersion) + ->withStatus(intval($status)); + } + } - /** - * @param $name - * @param $value - * @return static - */ - public function withHeader($name, $value): static - { - if (!is_array($value)) { - $value = [$value]; - } - $this->headers[$name] = $value; - return $this; - } + /** + * @param $key + * @param $value + */ + private function addRequestHeader($key, $value) + { + $this->headers[$key] = [$value]; + } - /** - * @param $name - * @param $value - * @return static - * @throws - */ - public function withAddedHeader($name, $value): static - { - if (!array_key_exists($name, $this->headers)) { - throw new \Exception('Headers `' . $name . '` not exists.'); - } - $this->headers[$name][] = $value; - return $this; - } + /** + * @param $name + * @return string|null + */ + #[Pure] public function getHeaderLine($name): string|null + { + if ($this->hasHeader($name)) { + return implode(';', $this->headers[$name]); + } + return null; + } - /** - * @param $name - * @return static - */ - public function withoutHeader($name): static - { - unset($this->headers[$name]); - return $this; - } + /** + * @return string|null + */ + #[Pure] public function getContentType(): ?string + { + return $this->getHeaderLine('Content-Type'); + } - /** - * @return null|array - */ - public function getCookieParams(): ?array - { - return $this->cookieParams; - } + /** + * @param $name + * @param $value + * @return static + */ + public function withHeader($name, $value): static + { + if (!is_array($value)) { + $value = [$value]; + } + $this->headers[$name] = $value; + return $this; + } - /** - * @param array|null $cookies - * @return static - */ - public function withCookieParams(?array $cookies): static - { - $this->cookieParams = $cookies; - return $this; - } - - /** - * @return StreamInterface - */ - public function getBody(): StreamInterface - { - return $this->stream; - } + /** + * @param $name + * @param $value + * @return static + * @throws + */ + public function withAddedHeader($name, $value): static + { + if (!array_key_exists($name, $this->headers)) { + throw new \Exception('Headers `' . $name . '` not exists.'); + } + $this->headers[$name][] = $value; + return $this; + } - /** - * @param StreamInterface $body - * @return static - */ - public function withBody(StreamInterface $body): static - { - $this->stream = $body; - return $this; - } + /** + * @param $name + * @return static + */ + public function withoutHeader($name): static + { + unset($this->headers[$name]); + return $this; + } - /** - * @return string|null - */ - #[Pure] public function getAccessControlAllowOrigin(): ?string - { - return $this->getHeaderLine('Access-Control-Allow-Origin'); - } + /** + * @return null|array + */ + public function getCookieParams(): ?array + { + return $this->cookieParams; + } - /** - * @return string|null - */ - #[Pure] public function getAccessControlAllowHeaders(): ?string - { - return $this->getHeaderLine('Access-Control-Allow-Headers'); - } + /** + * @param array|null $cookies + * @return static + */ + public function withCookieParams(?array $cookies): static + { + $this->cookieParams = $cookies; + return $this; + } + + /** + * @return StreamInterface + */ + public function getBody(): StreamInterface + { + return $this->stream; + } - /** - * @return string|null - */ - #[Pure] public function getAccessControlRequestMethod(): ?string - { - return $this->getHeaderLine('Access-Control-Request-Method'); - } + /** + * @param StreamInterface $body + * @return static + */ + public function withBody(StreamInterface $body): static + { + $this->stream = $body; + return $this; + } + + + /** + * @return string|null + */ + #[Pure] public function getAccessControlAllowOrigin(): ?string + { + return $this->getHeaderLine('Access-Control-Allow-Origin'); + } + + + /** + * @return string|null + */ + #[Pure] public function getAccessControlAllowHeaders(): ?string + { + return $this->getHeaderLine('Access-Control-Allow-Headers'); + } + + + /** + * @return string|null + */ + #[Pure] public function getAccessControlRequestMethod(): ?string + { + return $this->getHeaderLine('Access-Control-Request-Method'); + } + + + protected function setStore($key, callable $callback) + { + + } } diff --git a/http-message/Request.php b/http-message/Request.php index 68e19e07..d33eb399 100644 --- a/http-message/Request.php +++ b/http-message/Request.php @@ -11,7 +11,7 @@ use Psr\Http\Message\UriInterface; /** * */ -abstract class Request implements RequestInterface +class Request implements RequestInterface { use Message; diff --git a/http-message/ServerRequest.php b/http-message/ServerRequest.php index eb83afaf..56a4808e 100644 --- a/http-message/ServerRequest.php +++ b/http-message/ServerRequest.php @@ -14,193 +14,195 @@ class ServerRequest extends Request implements ServerRequestInterface { - /** - * @var mixed - */ - protected ?array $parsedBody = null; + const PARSE_BODY = 'with.parsed.body.callback'; - /** - * @var array|null - */ - protected ?array $serverParams; + /** + * @var mixed + */ + protected ?array $parsedBody = null; - /** - * @var array|null - */ - protected ?array $queryParams; - - /** - * @var array|null - */ - protected ?array $uploadedFiles; + /** + * @var array|null + */ + protected ?array $serverParams; - protected \Swoole\Http\Request $serverTarget; + /** + * @var array|null + */ + protected ?array $queryParams; + + /** + * @var array|null + */ + protected ?array $uploadedFiles; - /** - * @param array $server - * @return static - */ - public function withServerParams(array $server): static - { - $this->serverParams = $server; - return $this; - } - - /** - * @param \Swoole\Http\Request $server - * @return static - */ - public function withServerTarget(\Swoole\Http\Request $server): static - { - $this->serverTarget = $server; - return $this; - } + protected \Swoole\Http\Request $serverTarget; - /** - * @param \Swoole\Http\Request $request - * @return static|ServerRequestInterface - */ - public static function createServerRequest(\Swoole\Http\Request $request): static|ServerRequestInterface - { - return (new static())->parseRequestHeaders($request) - ->withServerParams($request->server) - ->withServerTarget($request) - ->withCookieParams($request->cookie) - ->withUri(Uri::parseUri($request)) - ->withBody(new Stream($request->getContent())) - ->withQueryParams($request->get ?? []) - ->withUploadedFiles($request->files ?? []) - ->withMethod($request->getMethod()) - ->withParsedBody(function (StreamInterface $stream, ?array $posts) { - try { - $content = Parse::data($stream->getContents()); - if (!empty($content)) { - return $content; - } - return $posts; - } catch (\Throwable $throwable) { - return $posts; - } - }); - } + /** + * @param array $server + * @return static + */ + public function withServerParams(array $server): static + { + $this->serverParams = $server; + return $this; + } + + /** + * @param \Swoole\Http\Request $server + * @return static + */ + public function withServerTarget(\Swoole\Http\Request $server): static + { + $this->serverTarget = $server; + return $this; + } - /** - * @return null|array - */ - public function getServerParams(): ?array - { - return $this->serverParams; - } + /** + * @param \Swoole\Http\Request $request + * @return static|ServerRequestInterface + * @throws \Exception + */ + public static function createServerRequest(\Swoole\Http\Request $request): static|ServerRequestInterface + { + $serverRequest = new ServerRequest(); + $serverRequest->withData($request->getData()); + $serverRequest->withServerParams($request->server); + $serverRequest->withServerTarget($request); + $serverRequest->withCookieParams($request->cookie); + $serverRequest->withUri(Uri::parseUri($request)); + $serverRequest->withQueryParams($request->get ?? []); + $serverRequest->withUploadedFiles($request->files ?? []); + $serverRequest->withMethod($request->getMethod()); + $serverRequest->withParsedBody($request->post); + return $serverRequest; + } - /** - * @return array|null - */ - public function getQueryParams(): ?array - { - return $this->queryParams; - } + /** + * @return null|array + */ + public function getServerParams(): ?array + { + return $this->serverParams; + } - /** - * @param array $query - * @return ServerRequestInterface - */ - public function withQueryParams(array $query): ServerRequestInterface - { - $this->queryParams = $query; - return $this; - } + /** + * @return array|null + */ + public function getQueryParams(): ?array + { + return $this->queryParams; + } - /** - * @return array|null - */ - public function getUploadedFiles(): ?array - { - return $this->uploadedFiles; - } + /** + * @param array $query + * @return ServerRequestInterface + */ + public function withQueryParams(array $query): ServerRequestInterface + { + $this->queryParams = $query; + return $this; + } - /** - * @param array $uploadedFiles - * @return ServerRequestInterface - */ - public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface - { - $this->uploadedFiles = $uploadedFiles; - return $this; - } + /** + * @return array|null + */ + public function getUploadedFiles(): ?array + { + return $this->uploadedFiles; + } - /** - * @return array|object|null - */ - public function getParsedBody(): object|array|null - { - if (empty($this->parsedBody)) { - $callback = Context::getContext('with.parsed.body.callback'); - - $this->parsedBody = $callback($this->getBody(), $this->serverTarget->post); - } - return $this->parsedBody; - } + /** + * @param array $uploadedFiles + * @return ServerRequestInterface + */ + public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface + { + $this->uploadedFiles = $uploadedFiles; + return $this; + } - /** - * @param array|object|null $data - * @return ServerRequestInterface - */ - public function withParsedBody($data): ServerRequestInterface - { - Context::setContext('with.parsed.body.callback', $data); - return $this; - } + /** + * @return array|object|null + */ + public function getParsedBody(): object|array|null + { + if (empty($this->parsedBody)) { + $callback = Context::getContext(self::PARSE_BODY); + + $this->parsedBody = $callback($this->getBody(), $this->serverTarget->post); + } + return $this->parsedBody; + } - /** - * @return array - */ - public function getAttributes(): array - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @param array|object|null $data + * @return ServerRequestInterface + */ + public function withParsedBody($data): ServerRequestInterface + { + $functions = function (StreamInterface $stream) use ($data) { + $content = Parse::data($stream->getContents()); + if (!empty($content)) { + return $content; + } + return $data; + }; + Context::setContext(self::PARSE_BODY, $functions); + return $this; + } - /** - * @param string $name - * @param null $default - * @return mixed - */ - public function getAttribute($name, $default = null): mixed - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @return array + */ + public function getAttributes(): array + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @param string $name - * @param mixed $value - * @return ServerRequestInterface - */ - public function withAttribute($name, $value): ServerRequestInterface - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @param string $name + * @param null $default + * @return mixed + */ + public function getAttribute($name, $default = null): mixed + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @param string $name - * @return ServerRequestInterface - */ - public function withoutAttribute($name): ServerRequestInterface - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @param string $name + * @param mixed $value + * @return ServerRequestInterface + */ + public function withAttribute($name, $value): ServerRequestInterface + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } + + + /** + * @param string $name + * @return ServerRequestInterface + */ + public function withoutAttribute($name): ServerRequestInterface + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } } diff --git a/http-message/Uri.php b/http-message/Uri.php index ccead081..00319ce3 100644 --- a/http-message/Uri.php +++ b/http-message/Uri.php @@ -10,246 +10,247 @@ class Uri implements UriInterface { - protected string $scheme = ''; - protected string $host = ''; - protected string $path = ''; - protected string $fragment = ''; - protected int $port = 80; + protected string $scheme = ''; + protected string $host = ''; + protected string $path = ''; + protected string $fragment = ''; + protected int $port = 80; - protected string $queryString = ''; + protected string $queryString = ''; - /** - * @return string - */ - public function getScheme(): string - { - return $this->scheme; - } + /** + * @return string + */ + public function getScheme(): string + { + return $this->scheme; + } - /** - * @return string - */ - public function getAuthority(): string - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @return string + */ + public function getAuthority(): string + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @return string - */ - public function getUserInfo(): string - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @return string + */ + public function getUserInfo(): string + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @return string - */ - public function getHost(): string - { - return $this->host; - } - - /** - * @return int|null - */ - public function getPort(): ?int - { - return $this->port; - } + /** + * @return string + */ + public function getHost(): string + { + return $this->host; + } - /** - * @return string - */ - public function getPath(): string - { - return $this->path; - } + /** + * @return int|null + */ + public function getPort(): ?int + { + return $this->port; + } - /** - * @return string - */ - public function getQuery(): string - { - return $this->queryString; - } + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } - /** - * @return string - */ - public function getFragment(): string - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @return string + */ + public function getQuery(): string + { + return $this->queryString; + } - /** - * @param string $scheme - * @return $this|Uri - */ - public function withScheme($scheme): UriInterface - { - $this->scheme = $scheme; - return $this; - } + /** + * @return string + */ + public function getFragment(): string + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @param string $user - * @param null $password - * @return Uri - */ - public function withUserInfo($user, $password = null): UriInterface - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @param string $scheme + * @return $this|Uri + */ + public function withScheme($scheme): UriInterface + { + $this->scheme = $scheme; + return $this; + } - /** - * @param string $host - * @return $this|Uri - */ - public function withHost($host): UriInterface - { - $this->host = $host; - return $this; - } + /** + * @param string $user + * @param null $password + * @return Uri + */ + public function withUserInfo($user, $password = null): UriInterface + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @param int|null $port - * @return $this|Uri - */ - public function withPort($port): UriInterface - { - $this->port = $port; - return $this; - } + /** + * @param string $host + * @return $this|Uri + */ + public function withHost($host): UriInterface + { + $this->host = $host; + return $this; + } - /** - * @param string $path - * @return $this|Uri - */ - public function withPath($path): UriInterface - { - $this->path = $path; - return $this; - } + /** + * @param int|null $port + * @return $this|Uri + */ + public function withPort($port): UriInterface + { + $this->port = $port; + return $this; + } - /** - * @param string $query - * @return $this|Uri - */ - public function withQuery($query): UriInterface - { - $this->queryString = $query; - return $this; - } + /** + * @param string $path + * @return $this|Uri + */ + public function withPath($path): UriInterface + { + $this->path = $path; + return $this; + } - /** - * @param string $fragment - * @return Uri - */ - public function withFragment($fragment): UriInterface - { - throw new \BadMethodCallException('Not Accomplish Method.'); - } + /** + * @param string $query + * @return $this|Uri + */ + public function withQuery($query): UriInterface + { + $this->queryString = $query; + return $this; + } - /** - * @return string - */ - public function __toString(): string - { - $domain = sprintf('%s://%s', $this->scheme, $this->host); - if (!in_array($this->port, [80, 443])) { - $domain .= ':' . $this->port; - } - if (empty($this->query) && empty($this->fragment)) { - return $domain . $this->path; - } - return sprintf('%s?%s#%s', $domain . $this->path, - $this->queryString, $this->fragment); - } + /** + * @param string $fragment + * @return Uri + */ + public function withFragment($fragment): UriInterface + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } - /** - * @return int - */ - public function getDefaultPort(): int - { - return $this->scheme == 'https' ? 443 : 80; - } + /** + * @return string + */ + public function __toString(): string + { + $domain = sprintf('%s://%s', $this->scheme, $this->host); + if (!in_array($this->port, [80, 443])) { + $domain .= ':' . $this->port; + } + if (empty($this->query) && empty($this->fragment)) { + return $domain . $this->path; + } + return sprintf('%s?%s#%s', $domain . $this->path, + $this->queryString, $this->fragment); + } - /** - * @param Request $request - * @return UriInterface - */ - public static function parseUri(Request $request): UriInterface - { - $server = $request->server; - $header = $request->header; - $uri = new static(); - $uri = $uri->withScheme(!empty($server['https']) && $server['https'] !== 'off' ? 'https' : 'http'); - if (isset($request->header['x-forwarded-proto'])) { - $uri->withScheme($request->header['x-forwarded-proto'])->withPort(443); - } + /** + * @return int + */ + public function getDefaultPort(): int + { + return $this->scheme == 'https' ? 443 : 80; + } - $hasPort = false; - if (isset($server['http_host'])) { - $hostHeaderParts = explode(':', $server['http_host']); - $uri = $uri->withHost($hostHeaderParts[0]); - if (isset($hostHeaderParts[1])) { - $hasPort = true; - $uri = $uri->withPort($hostHeaderParts[1]); - } - } elseif (isset($server['server_name'])) { - $uri = $uri->withHost($server['server_name']); - } elseif (isset($server['server_addr'])) { - $uri = $uri->withHost($server['server_addr']); - } elseif (isset($header['host'])) { - $hasPort = true; - if (strpos($header['host'], ':')) { - [$host, $port] = explode(':', $header['host'], 2); - if ($port != $uri->getDefaultPort()) { - $uri = $uri->withPort($port); - } - } else { - $host = $header['host']; - } - $uri = $uri->withHost($host); - } + /** + * @param Request $request + * @return UriInterface + */ + public static function parseUri(Request $request): UriInterface + { + $server = $request->server; + $header = $request->header; + $uri = new static(); + $uri = $uri->withScheme(!empty($server['https']) && $server['https'] !== 'off' ? 'https' : 'http'); + if (isset($request->header['x-forwarded-proto'])) { + $uri->withScheme($request->header['x-forwarded-proto'])->withPort(443); + } - if (!$hasPort && isset($server['server_port'])) { - $uri = $uri->withPort($server['server_port']); - } + $hasPort = false; + if (isset($server['http_host'])) { + $hostHeaderParts = explode(':', $server['http_host']); + $uri = $uri->withHost($hostHeaderParts[0]); + if (isset($hostHeaderParts[1])) { + $hasPort = true; + $uri = $uri->withPort($hostHeaderParts[1]); + } + } elseif (isset($server['server_name'])) { + $uri = $uri->withHost($server['server_name']); + } elseif (isset($server['server_addr'])) { + $uri = $uri->withHost($server['server_addr']); + } elseif (isset($header['host'])) { + $hasPort = true; + if (strpos($header['host'], ':')) { + [$host, $port] = explode(':', $header['host'], 2); + if ($port != $uri->getDefaultPort()) { + $uri = $uri->withPort($port); + } + } else { + $host = $header['host']; + } - $hasQuery = false; - if (isset($server['request_uri'])) { - $requestUriParts = explode('?', $server['request_uri']); - $uri = $uri->withPath($requestUriParts[0]); - if (isset($requestUriParts[1])) { - $hasQuery = true; - $uri = $uri->withQuery($requestUriParts[1]); - } - } + $uri = $uri->withHost($host); + } - if (!$hasQuery && isset($server['query_string'])) { - $uri = $uri->withQuery($server['query_string']); - } + if (!$hasPort && isset($server['server_port'])) { + $uri = $uri->withPort($server['server_port']); + } - return $uri; - } + $hasQuery = false; + if (isset($server['request_uri'])) { + $requestUriParts = explode('?', $server['request_uri']); + $uri = $uri->withPath($requestUriParts[0]); + if (isset($requestUriParts[1])) { + $hasQuery = true; + $uri = $uri->withQuery($requestUriParts[1]); + } + } + + if (!$hasQuery && isset($server['query_string'])) { + $uri = $uri->withQuery($server['query_string']); + } + + return $uri; + } } diff --git a/http-server/Constrict/Request.php b/http-server/Constrict/Request.php index 549d732e..15384efe 100644 --- a/http-server/Constrict/Request.php +++ b/http-server/Constrict/Request.php @@ -49,10 +49,11 @@ class Request implements RequestInterface } - /** - * @param \Swoole\Http\Request $request - * @return Request - */ + /** + * @param \Swoole\Http\Request $request + * @return Request + * @throws \Exception + */ public static function create(\Swoole\Http\Request $request): Request { $serverRequest = ServerRequest::createServerRequest($request); diff --git a/http-server/Service/Http.php b/http-server/Service/Http.php index 9235f67a..93dd4918 100644 --- a/http-server/Service/Http.php +++ b/http-server/Service/Http.php @@ -42,7 +42,7 @@ class Http extends \Server\Abstracts\Http implements OnClose, OnConnect public function onRequest(Request $request, Response $response): void { try { - $node = $this->router->Branch_search($Psr7Request = ScRequest::create($request)); + $node = $this->router->radix_tree($Psr7Request = ScRequest::create($request)); if (!($node instanceof Node)) { throw new RequestException(Constant::STATUS_404_MESSAGE, 404); }