This commit is contained in:
2023-04-15 23:31:16 +08:00
parent 2d9ac93a7a
commit 41a53200b3
69 changed files with 1678 additions and 749 deletions
+24
View File
@@ -0,0 +1,24 @@
{
"name": "game-worker/kiri-router",
"description": "kiri router",
"authors": [
{
"name": "XiangLin",
"email": "as2252258@163.com"
}
],
"license": "MIT",
"require": {
"php": ">=8.0",
"composer-runtime-api": "^2.0",
"psr/http-server-middleware": "^1.0",
"psr/http-message": "^1.0"
},
"autoload": {
"psr-4": {
"Kiri\\Router": "./src"
}
},
"require-dev": {
}
}
+33
View File
@@ -5,4 +5,37 @@ namespace Kiri\Router;
class ActionManager
{
private static array $array = [];
/**
* @param string $class
* @param string $method
* @param Handler $handler
* @return void
*/
public static function add(string $class, string $method, Handler $handler): void
{
if (!isset(static::$array[$class])) {
static::$array[$class] = [$method => []];
}
static::$array[$class][$method] = $handler;
}
/**
* @param string $class
* @param string $method
* @return array|null
*/
public static function get(string $class, string $method): ?Handler
{
if (isset(static::$array[$class])) {
return static::$array[$class][$method] ?? null;
}
return null;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Aspect;
namespace Kiri\Router\Aspect;
/**
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Aspect;
namespace Kiri\Router\Aspect;
/**
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Aspect;
namespace Kiri\Router\Aspect;
interface OnAspectInterface
{
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Aspect;
namespace Kiri\Router\Aspect;
interface OnJoinPointInterface
{
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Aspect;
namespace Kiri\Router\Aspect;
+36 -1
View File
@@ -2,7 +2,42 @@
namespace Kiri\Router\Base;
class AbstractHandler
use Kiri\Inject\Route\TestResponse;
use Kiri\Router\Handler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
abstract class AbstractHandler
{
public int $offset = 0;
/**
* @param array $middlewares
* @param Handler $handler
*/
public function __construct(public array $middlewares, public Handler $handler)
{
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @throws \ReflectionException
*/
public function execute(ServerRequestInterface $request): ResponseInterface
{
if (!isset($this->middlewares[$this->offset])) {
return $this->handler->handle($request);
}
$middleware = di($this->middlewares[$this->offset]);
$this->offset += 1;
return $middleware->process($request);
}
}
+1 -1
View File
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
/**
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
class ClosureController extends Controller
{
+1 -1
View File
@@ -1,7 +1,7 @@
<?php
declare(strict_types=1);
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
use Kiri;
+9 -10
View File
@@ -1,19 +1,19 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
use Exception;
use Kiri\Message\Handler\Abstracts\Middleware;
use Kiri\Message\ServerRequest;
use Kiri\Router\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
*
*/
class CoreMiddleware extends Middleware
class CoreMiddleware implements MiddlewareInterface
{
@@ -25,12 +25,11 @@ class CoreMiddleware extends Middleware
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$requestMethod = $request->getAccessControlRequestMethod();
$allowHeaders = $request->getAccessControlAllowHeaders();
$this->response->withAccessControlAllowOrigin('*')->withAccessControlRequestMethod($requestMethod)
->withAccessControlAllowHeaders($allowHeaders);
/** @var ResponseInterface $response */
$response = \Kiri::service()->get('response');
$response->withHeader('Access-Control-Allow-Headers', $request->header('Access-Control-Allow-Headers'))
->withHeader('Access-Control-Request-Method', $request->header('Access-Control-Allow-Origin'))
->withHeader('Access-Control-Allow-Origin', $request->header('Access-Control-Allow-Headers'));
return $handler->handle($request);
}
+1 -3
View File
@@ -1,9 +1,7 @@
<?php
namespace Kiri\Message\Abstracts;
namespace Kiri\Router\Base;
use Kiri\Annotation\Inject;
use Kiri\Events\EventDispatch;
trait EventDispatchHelper
{
+11 -10
View File
@@ -1,12 +1,13 @@
<?php
namespace Kiri\Message;
namespace Kiri\Router\Base;
use Kiri\Message\Constrict\Response;
use Kiri\Message\Constrict\ResponseInterface;
use Kiri\Message\Abstracts\ExceptionHandlerInterface;
use Kiri\Router\ContentType;
use Kiri\Router\Interface\ExceptionHandlerInterface;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use Kiri\Router\Constrict\Stream;
/**
@@ -18,17 +19,17 @@ class ExceptionHandlerDispatcher implements ExceptionHandlerInterface
/**
* @param Throwable $exception
* @param Response $response
* @param object $response
* @return ResponseInterface
*/
public function emit(Throwable $exception, Response $response): ResponseInterface
public function emit(Throwable $exception, object $response): ResponseInterface
{
$response->withContentType(ContentType::HTML);
$response->withContentType(ContentType::HTML)->withBody(new Stream(jTraceEx($exception, null, true)));
if ($exception->getCode() == 404) {
return $response->withBody(new Stream($exception->getMessage()))->withStatus(404);
return $response->withStatus(404);
} else {
return $response->withStatus($exception->getCode() == 0 ? 500 : $exception->getCode());
}
return $response->withBody(new Stream(jTraceEx($exception, null, true)))
->withStatus($exception->getCode() == 0 ? 500 : $exception->getCode());
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
use Kiri\Message\Constrict\ResponseInterface;
+40
View File
@@ -2,7 +2,47 @@
namespace Kiri\Router\Base;
use Exception;
use Kiri\Core\HashMap;
use Psr\Http\Server\MiddlewareInterface;
class Middleware
{
public HashMap $map;
public HashMap $routeMap;
/**
*
*/
public function __construct()
{
$this->routeMap = new HashMap();
$this->map = new HashMap();
}
/**
* @param string $path
* @param mixed $middleware
* @return void
* @throws Exception
*/
public function addPathMiddleware(string $path, string $middleware): void
{
if ($this->routeMap->has($path)) {
$values = $this->routeMap->get($path);
if (in_array($middleware, $values)) {
return;
}
if (!in_array(MiddlewareInterface::class, class_implements($middleware))) {
return;
}
}
$this->routeMap->append($path, $middleware);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router\Base;
use Kiri\Message\Constrict\ResponseInterface;
+70 -16
View File
@@ -1,13 +1,32 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use InvalidArgumentException;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\StreamInterface;
class Message implements MessageInterface
{
/**
* @var string
*/
private string $version = '1.1';
/**
* @var array
*/
private array $headers = [];
/**
* @var StreamInterface
*/
private StreamInterface $stream;
/**
* Retrieves the HTTP protocol version as a string.
*
@@ -15,9 +34,10 @@ class Message implements MessageInterface
*
* @return string HTTP protocol version.
*/
public function getProtocolVersion()
public function getProtocolVersion(): string
{
// TODO: Implement getProtocolVersion() method.
return $this->version;
}
/**
@@ -33,9 +53,11 @@ class Message implements MessageInterface
* @param string $version HTTP protocol version
* @return static
*/
public function withProtocolVersion(string $version)
public function withProtocolVersion(string $version): static
{
// TODO: Implement withProtocolVersion() method.
$this->version = $version;
return $this;
}
/**
@@ -63,9 +85,10 @@ class Message implements MessageInterface
* key MUST be a header name, and each value MUST be an array of strings
* for that header.
*/
public function getHeaders()
public function getHeaders(): array
{
// TODO: Implement getHeaders() method.
return $this->headers;
}
/**
@@ -76,9 +99,10 @@ class Message implements MessageInterface
* name using a case-insensitive string comparison. Returns false if
* no matching header name is found in the message.
*/
public function hasHeader(string $name)
public function hasHeader(string $name): bool
{
// TODO: Implement hasHeader() method.
return isset($this->headers[$name]) && $this->headers[$name] !== null;
}
/**
@@ -95,9 +119,15 @@ class Message implements MessageInterface
* header. If the header does not appear in the message, this method MUST
* return an empty array.
*/
public function getHeader(string $name)
public function getHeader(string $name): array
{
// TODO: Implement getHeader() method.
if (isset($this->headers[$name])) {
$header = $this->headers[$name];
return is_string($header) ? [$header] : $header;
}
return [];
}
/**
@@ -119,9 +149,24 @@ class Message implements MessageInterface
* concatenated together using a comma. If the header does not appear in
* the message, this method MUST return an empty string.
*/
public function getHeaderLine(string $name)
public function getHeaderLine(string $name): string
{
// TODO: Implement getHeaderLine() method.
return implode(';', $this->getHeader($name));
}
/**
* @param string $name
* @param string|null $default
* @return string|null
*/
public function header(string $name, ?string $default = null): ?string
{
if (!$this->hasHeader($name)) {
return $default;
}
return $this->getHeaderLine($name);
}
/**
@@ -137,11 +182,13 @@ class Message implements MessageInterface
* @param string $name Case-insensitive header field name.
* @param string|string[] $value Header value(s).
* @return static
* @throws \InvalidArgumentException for invalid header names or values.
* @throws InvalidArgumentException for invalid header names or values.
*/
public function withHeader(string $name, $value)
public function withHeader(string $name, $value): static
{
// TODO: Implement withHeader() method.
// TODO: Implement withAddedHeader() method.
$this->headers[$name] = $value;
return $this;
}
/**
@@ -158,11 +205,13 @@ class Message implements MessageInterface
* @param string $name Case-insensitive header field name to add.
* @param string|string[] $value Header value(s).
* @return static
* @throws \InvalidArgumentException for invalid header names or values.
* @throws InvalidArgumentException for invalid header names or values.
*/
public function withAddedHeader(string $name, $value)
public function withAddedHeader(string $name, $value): static
{
// TODO: Implement withAddedHeader() method.
$this->headers[$name] = $value;
return $this;
}
/**
@@ -177,9 +226,11 @@ class Message implements MessageInterface
* @param string $name Case-insensitive header field name to remove.
* @return static
*/
public function withoutHeader(string $name)
public function withoutHeader(string $name): static
{
// TODO: Implement withoutHeader() method.
unset($this->headers[$name]);
return $this;
}
/**
@@ -187,9 +238,10 @@ class Message implements MessageInterface
*
* @return StreamInterface Returns the body as a stream.
*/
public function getBody()
public function getBody(): StreamInterface
{
// TODO: Implement getBody() method.
return $this->stream;
}
/**
@@ -203,10 +255,12 @@ class Message implements MessageInterface
*
* @param StreamInterface $body Body.
* @return static
* @throws \InvalidArgumentException When the body is not valid.
* @throws InvalidArgumentException When the body is not valid.
*/
public function withBody(StreamInterface $body)
public function withBody(StreamInterface $body): static
{
// TODO: Implement withBody() method.
$this->stream = $body;
return $this;
}
}
+41 -7
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
@@ -8,6 +8,19 @@ use Psr\Http\Message\UriInterface;
class Request extends Message implements RequestInterface
{
/**
* @var string
*/
private string $method;
/**
* @var UriInterface
*/
private UriInterface $uri;
/**
* Retrieves the message's request target.
*
@@ -24,9 +37,23 @@ class Request extends Message implements RequestInterface
*
* @return string
*/
public function getRequestTarget()
public function getRequestTarget(): string
{
// TODO: Implement getRequestTarget() method.
return (string)$this->getUri();
}
/**
* @param array $headers
* @return $this
*/
public function withHeaders(array $headers): static
{
foreach ($headers as $key => $header) {
$this->withHeader($key, [$header]);
}
return $this;
}
/**
@@ -46,9 +73,10 @@ class Request extends Message implements RequestInterface
* @param string $requestTarget
* @return static
*/
public function withRequestTarget(string $requestTarget)
public function withRequestTarget(string $requestTarget): static
{
// TODO: Implement withRequestTarget() method.
return $this;
}
/**
@@ -56,9 +84,10 @@ class Request extends Message implements RequestInterface
*
* @return string Returns the request method.
*/
public function getMethod()
public function getMethod(): string
{
// TODO: Implement getMethod() method.
return $this->method;
}
/**
@@ -76,9 +105,11 @@ class Request extends Message implements RequestInterface
* @return static
* @throws \InvalidArgumentException for invalid HTTP methods.
*/
public function withMethod(string $method)
public function withMethod(string $method): static
{
// TODO: Implement withMethod() method.
$this->method = $method;
return $this;
}
/**
@@ -90,9 +121,10 @@ class Request extends Message implements RequestInterface
* @return UriInterface Returns a UriInterface instance
* representing the URI of the request.
*/
public function getUri()
public function getUri(): UriInterface
{
// TODO: Implement getUri() method.
return $this->uri;
}
/**
@@ -125,8 +157,10 @@ class Request extends Message implements RequestInterface
* @param bool $preserveHost Preserve the original state of the Host header.
* @return static
*/
public function withUri(UriInterface $uri, bool $preserveHost = false)
public function withUri(UriInterface $uri, bool $preserveHost = false): static
{
// TODO: Implement withUri() method.
$this->uri = $uri;
return $this;
}
}
+27 -4
View File
@@ -1,12 +1,30 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use Kiri\Router\ContentType;
use Psr\Http\Message\ResponseInterface;
class Response extends Message implements ResponseInterface
{
private int $code = 200;
private string $reasonPhrase;
/**
* @param ContentType $type
* @return $this
*/
public function withContentType(ContentType $type): static
{
$this->withHeader('Content-Type', $type->name);
return $this;
}
/**
* Gets the response status code.
*
@@ -15,9 +33,10 @@ class Response extends Message implements ResponseInterface
*
* @return int Status code.
*/
public function getStatusCode()
public function getStatusCode(): int
{
// TODO: Implement getStatusCode() method.
return $this->code;
}
/**
@@ -40,9 +59,12 @@ class Response extends Message implements ResponseInterface
* @return static
* @throws \InvalidArgumentException For invalid status code arguments.
*/
public function withStatus(int $code, string $reasonPhrase = '')
public function withStatus(int $code, string $reasonPhrase = ''): static
{
// TODO: Implement withStatus() method.
$this->code = $code;
$this->reasonPhrase = $reasonPhrase;
return $this;
}
/**
@@ -58,8 +80,9 @@ class Response extends Message implements ResponseInterface
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @return string Reason phrase; must return an empty string if none present.
*/
public function getReasonPhrase()
public function getReasonPhrase(): string
{
// TODO: Implement getReasonPhrase() method.
return $this->reasonPhrase;
}
}
+60 -29
View File
@@ -1,12 +1,24 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use Psr\Http\Message\ServerRequestInterface;
class ServerRequest extends Request implements ServerRequestInterface
{
private array $posts = [];
private array $query = [];
private array $cookies = [];
private array $server = [];
private array $files = [];
/**
* Retrieve server parameters.
*
@@ -16,9 +28,10 @@ class ServerRequest extends Request implements ServerRequestInterface
*
* @return array
*/
public function getServerParams()
public function getServerParams(): array
{
// TODO: Implement getServerParams() method.
return $this->server;
}
/**
@@ -31,9 +44,10 @@ class ServerRequest extends Request implements ServerRequestInterface
*
* @return array
*/
public function getCookieParams()
public function getCookieParams(): array
{
// TODO: Implement getCookieParams() method.
return $this->cookies;
}
/**
@@ -53,9 +67,11 @@ class ServerRequest extends Request implements ServerRequestInterface
* @param array $cookies Array of key/value pairs representing cookies.
* @return static
*/
public function withCookieParams(array $cookies)
public function withCookieParams(array $cookies): static
{
// TODO: Implement withCookieParams() method.
$this->cookies = $cookies;
return $this;
}
/**
@@ -70,9 +86,10 @@ class ServerRequest extends Request implements ServerRequestInterface
*
* @return array
*/
public function getQueryParams()
public function getQueryParams(): array
{
// TODO: Implement getQueryParams() method.
return $this->query;
}
/**
@@ -97,9 +114,11 @@ class ServerRequest extends Request implements ServerRequestInterface
* $_GET.
* @return static
*/
public function withQueryParams(array $query)
public function withQueryParams(array $query): static
{
// TODO: Implement withQueryParams() method.
$this->query = $query;
return $this;
}
/**
@@ -114,9 +133,10 @@ class ServerRequest extends Request implements ServerRequestInterface
* @return array An array tree of UploadedFileInterface instances; an empty
* array MUST be returned if no data is present.
*/
public function getUploadedFiles()
public function getUploadedFiles(): array
{
// TODO: Implement getUploadedFiles() method.
return $this->files;
}
/**
@@ -130,9 +150,11 @@ class ServerRequest extends Request implements ServerRequestInterface
* @return static
* @throws \InvalidArgumentException if an invalid structure is provided.
*/
public function withUploadedFiles(array $uploadedFiles)
public function withUploadedFiles(array $uploadedFiles): static
{
// TODO: Implement withUploadedFiles() method.
$this->files = $uploadedFiles;
return $this;
}
/**
@@ -150,9 +172,10 @@ class ServerRequest extends Request implements ServerRequestInterface
* @return null|array|object The deserialized body parameters, if any.
* These will typically be an array or object.
*/
public function getParsedBody()
public function getParsedBody(): object|array|null
{
// TODO: Implement getParsedBody() method.
return $this->posts;
}
/**
@@ -183,9 +206,11 @@ class ServerRequest extends Request implements ServerRequestInterface
* @throws \InvalidArgumentException if an unsupported argument type is
* provided.
*/
public function withParsedBody($data)
public function withParsedBody($data): static
{
// TODO: Implement withParsedBody() method.
$this->posts = $data;
return $this;
}
/**
@@ -199,27 +224,31 @@ class ServerRequest extends Request implements ServerRequestInterface
*
* @return array Attributes derived from the request.
*/
public function getAttributes()
public function getAttributes(): array
{
// TODO: Implement getAttributes() method.
}/**
* Retrieve a single derived request attribute.
*
* Retrieves a single derived request attribute as described in
* getAttributes(). If the attribute has not been previously set, returns
* the default value as provided.
*
* This method obviates the need for a hasAttribute() method, as it allows
* specifying a default value to return if the attribute is not found.
*
* @param string $name The attribute name.
* @param mixed $default Default value to return if the attribute does not exist.
* @return mixed
* @see getAttributes()
*/
public function getAttribute(string $name, $default = null)
return [];
}
/**
* Retrieve a single derived request attribute.
*
* Retrieves a single derived request attribute as described in
* getAttributes(). If the attribute has not been previously set, returns
* the default value as provided.
*
* This method obviates the need for a hasAttribute() method, as it allows
* specifying a default value to return if the attribute is not found.
*
* @param string $name The attribute name.
* @param mixed $default Default value to return if the attribute does not exist.
* @return mixed
* @see getAttributes()
*/
public function getAttribute(string $name, $default = null): mixed
{
// TODO: Implement getAttribute() method.
return $default;
}
/**
@@ -237,9 +266,10 @@ class ServerRequest extends Request implements ServerRequestInterface
* @return static
* @see getAttributes()
*/
public function withAttribute(string $name, $value)
public function withAttribute(string $name, $value): static
{
// TODO: Implement withAttribute() method.
return $this;
}
/**
@@ -256,8 +286,9 @@ class ServerRequest extends Request implements ServerRequestInterface
* @return static
* @see getAttributes()
*/
public function withoutAttribute(string $name)
public function withoutAttribute(string $name): static
{
// TODO: Implement withoutAttribute() method.
return $this;
}
}
+86 -25
View File
@@ -1,12 +1,27 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use Psr\Http\Message\StreamInterface;
class Stream implements StreamInterface
{
public string $content = '';
public int $size = 0;
/**
* @param string $content
*/
public function __construct(string $content = '')
{
$this->content = $content;
}
/**
* Reads all data from the stream into a string, from the beginning to end.
*
@@ -24,6 +39,7 @@ class Stream implements StreamInterface
public function __toString()
{
// TODO: Implement __toString() method.
return $this->content;
}
/**
@@ -31,9 +47,14 @@ class Stream implements StreamInterface
*
* @return void
*/
public function close()
public function close(): void
{
// TODO: Implement close() method.
if (is_resource($this->content)) {
fclose($this->content);
} else {
$this->content = '';
}
}
/**
@@ -43,9 +64,10 @@ class Stream implements StreamInterface
*
* @return resource|null Underlying PHP stream, if any
*/
public function detach()
public function detach(): mixed
{
// TODO: Implement detach() method.
return null;
}
/**
@@ -53,9 +75,10 @@ class Stream implements StreamInterface
*
* @return int|null Returns the size in bytes if known, or null if unknown.
*/
public function getSize()
public function getSize(): ?int
{
// TODO: Implement getSize() method.
return $this->size;
}
/**
@@ -64,9 +87,10 @@ class Stream implements StreamInterface
* @return int Position of the file pointer
* @throws \RuntimeException on error.
*/
public function tell()
public function tell(): int
{
// TODO: Implement tell() method.
return 0;
}
/**
@@ -74,9 +98,10 @@ class Stream implements StreamInterface
*
* @return bool
*/
public function eof()
public function eof(): bool
{
// TODO: Implement eof() method.
return false;
}
/**
@@ -84,9 +109,10 @@ class Stream implements StreamInterface
*
* @return bool
*/
public function isSeekable()
public function isSeekable(): bool
{
// TODO: Implement isSeekable() method.
return true;
}
/**
@@ -101,22 +127,28 @@ class Stream implements StreamInterface
* SEEK_END: Set position to end-of-stream plus offset.
* @throws \RuntimeException on failure.
*/
public function seek(int $offset, int $whence = SEEK_SET)
public function seek(int $offset, int $whence = SEEK_SET): void
{
// TODO: Implement seek() method.
}/**
* Seek to the beginning of the stream.
*
* If the stream is not seekable, this method will raise an exception;
* otherwise, it will perform a seek(0).
*
* @throws \RuntimeException on failure.
* @link http://www.php.net/manual/en/function.fseek.php
* @see seek()
*/
public function rewind()
if (is_resource($this->content)) {
fseek($this->content, $offset, $whence);
}
}
/**
* Seek to the beginning of the stream.
*
* If the stream is not seekable, this method will raise an exception;
* otherwise, it will perform a seek(0).
*
* @throws \RuntimeException on failure.
* @link http://www.php.net/manual/en/function.fseek.php
* @see seek()
*/
public function rewind(): void
{
// TODO: Implement rewind() method.
$this->seek(0);
}
/**
@@ -124,9 +156,13 @@ class Stream implements StreamInterface
*
* @return bool
*/
public function isWritable()
public function isWritable(): bool
{
// TODO: Implement isWritable() method.
if (is_resource($this->content)) {
return is_writable($this->content);
}
return true;
}
/**
@@ -136,9 +172,17 @@ class Stream implements StreamInterface
* @return int Returns the number of bytes written to the stream.
* @throws \RuntimeException on failure.
*/
public function write(string $string)
public function write(string $string): int
{
// TODO: Implement write() method.
if (is_resource($this->content)) {
$this->content = fopen($string, 'wr');
$this->size = filesize($string);
} else {
$this->content = $string;
$this->size = mb_strlen($string);
}
return $this->size;
}
/**
@@ -146,9 +190,13 @@ class Stream implements StreamInterface
*
* @return bool
*/
public function isReadable()
public function isReadable(): bool
{
// TODO: Implement isReadable() method.
if (is_resource($this->content)) {
return is_readable($this->content);
}
return true;
}
/**
@@ -161,9 +209,14 @@ class Stream implements StreamInterface
* if no bytes are available.
* @throws \RuntimeException if an error occurs.
*/
public function read(int $length)
public function read(int $length): string
{
// TODO: Implement read() method.
if (!is_resource($this->content)) {
return mb_substr($this->content, 0, $length);
} else {
return fread($this->content, $length);
}
}
/**
@@ -173,9 +226,13 @@ class Stream implements StreamInterface
* @throws \RuntimeException if unable to read or an error occurs while
* reading.
*/
public function getContents()
public function getContents(): string
{
// TODO: Implement getContents() method.
if (is_resource($this->content)) {
return fread($this->content, $this->getSize());
}
return $this->content;
}
/**
@@ -190,8 +247,12 @@ class Stream implements StreamInterface
* provided. Returns a specific key value if a key is provided and the
* value is found, or null if the key is not found.
*/
public function getMetadata(?string $key = null)
public function getMetadata(?string $key = null): mixed
{
// TODO: Implement getMetadata() method.
if (is_resource($this->content)) {
return stream_get_meta_data($this->content);
}
return null;
}
}
+69 -16
View File
@@ -1,12 +1,21 @@
<?php
namespace Kiri\Router\Message;
namespace Kiri\Router\Constrict;
use Psr\Http\Message\UriInterface;
class Uri implements UriInterface
{
private string $scheme = '';
private string $host = '';
private int $port = 80;
private string $path = '';
private string $user = '';
private string $password = '';
private string $queryString;
private string $fragment;
/**
* Retrieve the scheme component of the URI.
*
@@ -21,9 +30,10 @@ class Uri implements UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.1
* @return string The URI scheme.
*/
public function getScheme()
public function getScheme(): string
{
// TODO: Implement getScheme() method.
return $this->scheme;
}
/**
@@ -44,9 +54,10 @@ class Uri implements UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.2
* @return string The URI authority, in "[user-info@]host[:port]" format.
*/
public function getAuthority()
public function getAuthority(): string
{
// TODO: Implement getAuthority() method.
return '';
}
/**
@@ -64,9 +75,10 @@ class Uri implements UriInterface
*
* @return string The URI user information, in "username[:password]" format.
*/
public function getUserInfo()
public function getUserInfo(): string
{
// TODO: Implement getUserInfo() method.
return $this->user . '[' . $this->password . ']';
}
/**
@@ -80,9 +92,10 @@ class Uri implements UriInterface
* @see http://tools.ietf.org/html/rfc3986#section-3.2.2
* @return string The URI host.
*/
public function getHost()
public function getHost(): string
{
// TODO: Implement getHost() method.
return $this->host;
}
/**
@@ -100,9 +113,10 @@ class Uri implements UriInterface
*
* @return null|int The URI port.
*/
public function getPort()
public function getPort(): ?int
{
// TODO: Implement getPort() method.
return $this->port;
}
/**
@@ -130,9 +144,10 @@ class Uri implements UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.3
* @return string The URI path.
*/
public function getPath()
public function getPath(): string
{
// TODO: Implement getPath() method.
return $this->path;
}
/**
@@ -155,9 +170,10 @@ class Uri implements UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.4
* @return string The URI query string.
*/
public function getQuery()
public function getQuery(): string
{
// TODO: Implement getQuery() method.
return $this->queryString;
}
/**
@@ -176,9 +192,10 @@ class Uri implements UriInterface
* @see https://tools.ietf.org/html/rfc3986#section-3.5
* @return string The URI fragment.
*/
public function getFragment()
public function getFragment(): string
{
// TODO: Implement getFragment() method.
return $this->fragment;
}
/**
@@ -196,9 +213,11 @@ class Uri implements UriInterface
* @return static A new instance with the specified scheme.
* @throws \InvalidArgumentException for invalid or unsupported schemes.
*/
public function withScheme(string $scheme)
public function withScheme(string $scheme): static
{
// TODO: Implement withScheme() method.
$this->scheme = $scheme;
return $this;
}
/**
@@ -215,9 +234,12 @@ class Uri implements UriInterface
* @param null|string $password The password associated with $user.
* @return static A new instance with the specified user information.
*/
public function withUserInfo(string $user, ?string $password = null)
public function withUserInfo(string $user, ?string $password = null): static
{
// TODO: Implement withUserInfo() method.
$this->user = $user;
$this->password = $password;
return $this;
}
/**
@@ -232,9 +254,11 @@ class Uri implements UriInterface
* @return static A new instance with the specified host.
* @throws \InvalidArgumentException for invalid hostnames.
*/
public function withHost(string $host)
public function withHost(string $host): static
{
// TODO: Implement withHost() method.
$this->host = $host;
return $this;
}
/**
@@ -254,9 +278,11 @@ class Uri implements UriInterface
* @return static A new instance with the specified port.
* @throws \InvalidArgumentException for invalid ports.
*/
public function withPort(?int $port)
public function withPort(?int $port): static
{
// TODO: Implement withPort() method.
$this->port = $port;
return $this;
}
/**
@@ -281,9 +307,11 @@ class Uri implements UriInterface
* @return static A new instance with the specified path.
* @throws \InvalidArgumentException for invalid paths.
*/
public function withPath(string $path)
public function withPath(string $path): static
{
// TODO: Implement withPath() method.
$this->path = $path;
return $this;
}
/**
@@ -301,9 +329,11 @@ class Uri implements UriInterface
* @return static A new instance with the specified query string.
* @throws \InvalidArgumentException for invalid query strings.
*/
public function withQuery(string $query)
public function withQuery(string $query): static
{
// TODO: Implement withQuery() method.
$this->queryString = $query;
return $this;
}
/**
@@ -320,9 +350,11 @@ class Uri implements UriInterface
* @param string $fragment The fragment to use with the new instance.
* @return static A new instance with the specified fragment.
*/
public function withFragment(string $fragment)
public function withFragment(string $fragment): static
{
// TODO: Implement withFragment() method.
$this->fragment = $fragment;
return $this;
}
/**
@@ -351,5 +383,26 @@ class Uri implements UriInterface
public function __toString()
{
// TODO: Implement __toString() method.
return $this->scheme . '://x.x.x.x:' . $this->port . '/' . $this->path . '?' . $this->queryString;
}
/**
* @param \Swoole\Http\Request $request
* @return UriInterface
*/
public static function parse(\Swoole\Http\Request $request): UriInterface
{
$uri = new static();
$uri->queryString = $request->server['query_string'];
$uri->path = $request->server['path_info'];
$uri->port = $request->server['server_port'];
if (isset($request->server['https']) && $request->server['https'] !== 'off') {
$uri->scheme = 'https';
} else {
$uri->scheme = 'http';
}
return $uri;
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Message;
namespace Kiri\Router;
enum ContentType
+29 -78
View File
@@ -1,7 +1,8 @@
<?php
namespace Kiri\Router\Inject;
namespace Kiri\Router;
use Closure;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
@@ -11,19 +12,31 @@ class ControllerInterpreter
/**
* @param string $className
* @param object $class
* @param string|ReflectionMethod $method
* @param ReflectionClass|null $reflection
* @return void
* @return Handler
* @throws ReflectionException
*/
public function addRouteByString(string $className, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): void
public function addRouteByString(object $class, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): Handler
{
$class = \Kiri::getDi()->get($className);
if (is_null($reflection)) {
$reflection = \Kiri::getDi()->getReflect($className);
$reflection = \Kiri::getDi()->getReflectionClass($class::class);
}
$this->resolveMethod($class, $method, $reflection);
return $this->resolveMethod($method, $reflection);
}
/**
* @param Closure $method
* @return Handler
* @throws ReflectionException
*/
public function addRouteByClosure(Closure $method): Handler
{
$reflection = \Kiri::getDi()->getFunctionParams($method);
return new Handler($method, $reflection);
}
@@ -31,96 +44,34 @@ class ControllerInterpreter
* @param object $class
* @param string|ReflectionMethod $method
* @param ReflectionClass|null $reflection
* @return void
* @return Handler
* @throws ReflectionException
*/
public function addRouteByObject(object $class, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): void
public function addRouteByObject(object $class, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): Handler
{
if (is_null($reflection)) {
$reflection = \Kiri::getDi()->getReflect($class::class);
$reflection = \Kiri::getDi()->getReflectionClass($class::class);
}
$this->resolveMethod($class, $method, $reflection);
return $this->resolveMethod($method, $reflection);
}
/**
* @param object $class
* @param string|ReflectionMethod $reflectionMethod
* @param ReflectionClass $reflectionClass
* @return void
* @return Handler
* @throws ReflectionException
*/
public function resolveMethod(object $class, string|\ReflectionMethod $reflectionMethod, ReflectionClass $reflectionClass): void
public function resolveMethod(string|\ReflectionMethod $reflectionMethod, ReflectionClass $reflectionClass): Handler
{
if (is_string($reflectionMethod)) {
$reflectionMethod = $reflectionClass->getMethod($reflectionMethod);
}
$this->resolveProperties($reflectionClass, $class);
$container = \Kiri::getDi();
$parameters = $container->getMethodParams($reflectionMethod);
$parameters = $this->resolveMethodParams($reflectionMethod->getParameters());
$handler = new Handler([$reflectionClass->getName(), $reflectionMethod->getName()], $parameters);
ActionManager::add($reflectionClass->getName(), $reflectionMethod->getName(), $handler);
return new Handler([$reflectionClass->getName(), $reflectionMethod->getName()], $parameters);
}
/**
* @param ReflectionClass $reflectionClass
* @param object $class
* @return void
*/
public function resolveProperties(ReflectionClass $reflectionClass, object $class): void
{
$properties = $reflectionClass->getProperties();
foreach ($properties as $property) {
$propertyAttributes = $property->getAttributes();
foreach ($propertyAttributes as $attribute) {
$attribute->newInstance()->dispatch($class, $property->getName());
}
}
}
/**
* @param array $parameters
* @return array
*/
public function resolveMethodParams(array $parameters): array
{
$params = [];
foreach ($parameters as $parameter) {
$parameterAttributes = $parameter->getAttributes();
if (count($parameterAttributes) < 1) {
if ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} else if ($parameter->getType() === null) {
$value = $parameter->getType();
} else {
$value = $parameter->getType()->getName();
if (class_exists($value) || interface_exists($value)) {
$value = \Kiri::getDi()->get($value);
} else {
$value = match ($parameter->getType()) {
'string' => '',
'int', 'float' => 0,
'', null, 'object', 'mixed' => NULL,
'bool' => false,
'default' => null
};
}
}
$params[$parameter->getName()] = $value;
} else {
$attribute = $parameterAttributes[0]->newInstance();
$params[$parameter->getName()] = $attribute->dispatch();
}
}
return $params;
}
}
+4 -2
View File
@@ -1,8 +1,9 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router;
use Kiri;
use ReflectionException;
class DataGrip
{
@@ -13,11 +14,12 @@ class DataGrip
/**
* @param $type
* @return RouterCollector
* @throws ReflectionException
*/
public function get($type): RouterCollector
{
if (!isset($this->servers[$type])) {
$this->servers[$type] = Kiri::getDi()->create(RouterCollector::class);
$this->servers[$type] = Kiri::getDi()->make(RouterCollector::class);
}
return $this->servers[$type];
}
+33 -1
View File
@@ -2,7 +2,39 @@
namespace Kiri\Router;
class Handler
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use ReflectionException;
class Handler implements RequestHandlerInterface
{
/**
* @param array|\Closure $handler
* @param array $parameter
*/
public function __construct(public array|\Closure $handler, public array $parameter)
{
}
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @throws ReflectionException
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
// TODO: Implement handle() method.
$result = call_user_func($this->handler, ...$this->parameter);
if ($result instanceof ResponseInterface) {
return $result;
} else {
$response = \Kiri::getDi()->get(ResponseInterface::class);
return $response->rewrite();
}
}
}
+20 -1
View File
@@ -2,7 +2,26 @@
namespace Kiri\Router;
class HttpRequestHandler
use Kiri\Router\Base\AbstractHandler;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use ReflectionException;
class HttpRequestHandler extends AbstractHandler implements RequestHandlerInterface
{
/**
* @param ServerRequestInterface $request
* @return ResponseInterface
* @throws ReflectionException
*/
public function handle(ServerRequestInterface $request): ResponseInterface
{
// TODO: Implement handle() method.
return $this->execute($request);
}
}
+46 -1
View File
@@ -2,7 +2,52 @@
namespace Kiri\Router;
class HttpResponseEmitter
use Exception;
use Kiri\Di\Interface\ResponseEmitter;
use Psr\Http\Message\ResponseInterface;
class HttpResponseEmitter implements ResponseEmitter
{
/**
* @param Response $proxy
* @param object $response
* @return void
* @throws Exception
*/
public function sender(ResponseInterface $proxy, object $response): void
{
// TODO: Implement sender() method.
$this->writeParams($proxy, $response);
$proxy->write($response);
}
/**
* @param Response $proxy
* @param object $response
* @return void
* @throws Exception
*/
private function writeParams(ResponseInterface $proxy, object $response): void
{
$response->setStatusCode($proxy->getStatusCode());
/** @var ServerRequest $request */
$request = \Kiri::service()->get('request');
foreach ($request->getHeaders() as $name => $header) {
$response->header($name, implode(', ', $header));
}
$response->setStatusCode($proxy->getStatusCode());
foreach ($request->getCookieParams() as $cookie) {
$response->setCookie(...$cookie);
}
$response->header('Server', 'swoole');
$response->header('Swoole-Version', swoole_version());
}
}
+62 -1
View File
@@ -1,8 +1,69 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Kiri\Router\Base\Middleware as MiddlewareManager;
use Kiri\Router\Interface\ValidatorInterface;
use Kiri\Router\Validator\Validator;
use Kiri\Router\Validator\ValidatorMiddleware;
use ReflectionException;
abstract class AbstractRequestMethod
{
/**
* @param string $path
* @param string $formValidate
*/
public function __construct(readonly public string $path, public string $formValidate = '')
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws \Exception
*/
public function registerMiddleware(object $class, string $method): void
{
$reflectionMethod = \Kiri::getDi()->getMethod($class::class, $method);
$middleware = $reflectionMethod->getAttributes(Middleware::class);
$middlewareManager = \Kiri::getDi()->get(MiddlewareManager::class);
foreach ($middleware as $value) {
/** @var Middleware $instance */
$instance = $value->newInstance();
$middlewareManager->addPathMiddleware($this->path, $instance->middleware);
}
if ($this->formValidate !== '') {
$middlewareManager->addPathMiddleware($this->path, new ValidatorMiddleware($this->getFormRule()));
}
}
/**
* @return Validator|null
* @throws ReflectionException
*/
public function getFormRule(): ?Validator
{
$validator = new Validator();
$reflect = \Kiri::getDi()->getReflectionClass($this->formValidate);
$model = $reflect->newInstanceWithoutConstructor();
foreach ($reflect->getProperties() as $property) {
foreach ($property->getAttributes() as $attribute) {
$rule = $attribute->newInstance();
if ($rule instanceof ValidatorInterface) {
$validator->addRule($property->getName(), $model, $rule);
}
}
}
return $validator;
}
}
+9
View File
@@ -2,7 +2,16 @@
namespace Kiri\Router\Inject;
#[\Attribute(\Attribute::TARGET_METHOD)]
class Aspect
{
/**
* @param string $aspect
*/
public function __construct(readonly public string $aspect)
{
}
}
+1 -41
View File
@@ -1,49 +1,9 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_CLASS)]
class Controller
{
readonly public ?RequestInterface $request;
readonly public ResponseInterface $response;
/**
*
*/
public function __construct()
{
$this->request = \Kiri::getDi()->get(RequestInterface::class);
}
/**
* @param object $class
* @return void
* @throws ReflectionException
*/
public function dispatch(object $class): void
{
// TODO: Implement dispatch() method.
$reflectionClass = \Kiri::getDi()->getReflect($class::class);
$scheduler = \Kiri::getDi()->get(ControllerInterpreter::class);
foreach ($reflectionClass->getMethods() as $reflectionMethod) {
$scheduler->addRouteByObject($class, $reflectionMethod, $reflectionClass);
}
}
}
+10 -10
View File
@@ -1,33 +1,33 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Delete extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_DELETE, $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
#[\Attribute(\Attribute::TARGET_METHOD)]
+10 -11
View File
@@ -1,33 +1,32 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Get extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
* @throws ReflectionException
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_GET, $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+8 -10
View File
@@ -1,28 +1,25 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Head extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
@@ -30,4 +27,5 @@ class Head extends AbstractRequestMethod implements InjectRouteInterface
Router::addRoute(RequestMethod::REQUEST_HEAD, $this->path, [$class, $method]);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
#[\Attribute(\Attribute::TARGET_METHOD)]
+2 -5
View File
@@ -1,9 +1,8 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Kiri\Inject\InjectPropertyInterface;
use Kiri\Message\Handler\Abstracts\MiddlewareManager;
use Kiri\Di\Interface\InjectPropertyInterface;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Middleware implements InjectPropertyInterface
@@ -24,8 +23,6 @@ class Middleware implements InjectPropertyInterface
*/
public function dispatch(object $class, string $property): void
{
// TODO: Implement dispatch() method.
MiddlewareManager::add($class::class, $property, $this->middleware);
}
+10 -9
View File
@@ -1,33 +1,34 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Options extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_OPTIONS, $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+35 -1
View File
@@ -2,7 +2,41 @@
namespace Kiri\Router\Inject;
class Other
use Exception;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD)]
class Other extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $method
* @param string $path
* @param string $formValidate
*/
public function __construct(readonly public string $method, string $path, string $formValidate = '')
{
parent::__construct($path, $formValidate);
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute([$this->method], $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+10 -10
View File
@@ -1,33 +1,33 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Post extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_POST, $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+10 -11
View File
@@ -1,33 +1,32 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
use Exception;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
use Kiri\Router\InjectRouteInterface;
use Kiri\Router\Router;
use Kiri\Router\Base\Middleware as MiddlewareManager;
use ReflectionException;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Put extends AbstractRequestMethod implements InjectRouteInterface
{
/**
* @param string $path
* @param array $params
*/
public function __construct(public string $path, public array $params = [])
{
}
/**
* @param object $class
* @param string $method
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_PUT, $this->path, [$class, $method]);
$this->registerMiddleware($class, $method);
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Inject\Route;
namespace Kiri\Router\Inject;
#[\Attribute(\Attribute::TARGET_PARAMETER)]
+4 -5
View File
@@ -1,10 +1,9 @@
<?php
namespace Kiri\Message\Abstracts;
namespace Kiri\Router\Interface;
use Kiri\Message\Constrict\Response;
use Throwable;
use Kiri\Message\Constrict\ResponseInterface;
use Psr\Http\Message\ResponseInterface;
/**
*
@@ -15,9 +14,9 @@ interface ExceptionHandlerInterface
/**
* @param Throwable $exception
* @param Response $response
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function emit(Throwable $exception, Response $response): ResponseInterface;
public function emit(Throwable $exception, ResponseInterface $response): ResponseInterface;
}
+9
View File
@@ -2,7 +2,16 @@
namespace Kiri\Router\Interface;
use Swoole\Http\Request;
use Swoole\Http\Response;
interface OnRequestInterface
{
/**
* @param Request $request
* @param Response $response
*/
public function onRequest(Request $request, Response $response): void;
}
+1 -1
View File
@@ -1,6 +1,6 @@
<?php
namespace Kiri\Router\Validator\Inject;
namespace Kiri\Router\Interface;
interface ValidatorInterface
{
+321 -1
View File
@@ -2,7 +2,327 @@
namespace Kiri\Router;
class Response
use InvalidArgumentException;
use Kiri\Di\Context;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
class Response implements ResponseInterface
{
/**
* @var ContentType
*/
public ContentType $contentType = ContentType::JSON;
/**
* @param ContentType $contentType
* @return Response
*/
public function withContentType(ContentType $contentType): static
{
$this->contentType = $contentType;
return $this;
}
/**
* @param string $method
* @param mixed ...$params
* @return mixed
*/
private function __call__(string $method, ...$params): mixed
{
if (!Context::exists(ResponseInterface::class)) {
$response = Context::set(ResponseInterface::class, new static());
} else {
$response = Context::get(ResponseInterface::class);
}
return $response->{$method}(...$params);
}
/**
* Retrieves the HTTP protocol version as a string.
*
* The string MUST contain only the HTTP version number (e.g., "1.1", "1.0").
*
* @return string HTTP protocol version.
*/
public function getProtocolVersion(): string
{
// TODO: Implement getProtocolVersion() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specified HTTP protocol version.
*
* The version string MUST contain only the HTTP version number (e.g.,
* "1.1", "1.0").
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new protocol version.
*
* @param string $version HTTP protocol version
* @return static
*/
public function withProtocolVersion(string $version): ResponseInterface
{
// TODO: Implement withProtocolVersion() method.
return $this->__call__(__FUNCTION__, $version);
}
/**
* Retrieves all message header values.
*
* The keys represent the header name as it will be sent over the wire, and
* each value is an array of strings associated with the header.
*
* // Represent the headers as a string
* foreach ($message->getHeaders() as $name => $values) {
* echo $name . ": " . implode(", ", $values);
* }
*
* // Emit headers iteratively:
* foreach ($message->getHeaders() as $name => $values) {
* foreach ($values as $value) {
* header(sprintf('%s: %s', $name, $value), false);
* }
* }
*
* While header names are not case-sensitive, getHeaders() will preserve the
* exact case in which headers were originally specified.
*
* @return string[][] Returns an associative array of the message's headers. Each
* key MUST be a header name, and each value MUST be an array of strings
* for that header.
*/
public function getHeaders(): array
{
// TODO: Implement getHeaders() method.
return $this->__call__(__FUNCTION__);
}
/**
* Checks if a header exists by the given case-insensitive name.
*
* @param string $name Case-insensitive header field name.
* @return bool Returns true if any header names match the given header
* name using a case-insensitive string comparison. Returns false if
* no matching header name is found in the message.
*/
public function hasHeader(string $name): bool
{
// TODO: Implement hasHeader() method.
return $this->__call__(__FUNCTION__, $name);
}
/**
* Retrieves a message header value by the given case-insensitive name.
*
* This method returns an array of all the header values of the given
* case-insensitive header name.
*
* If the header does not appear in the message, this method MUST return an
* empty array.
*
* @param string $name Case-insensitive header field name.
* @return string[] An array of string values as provided for the given
* header. If the header does not appear in the message, this method MUST
* return an empty array.
*/
public function getHeader(string $name): array
{
// TODO: Implement getHeader() method.
return $this->__call__(__FUNCTION__, $name);
}
/**
* Retrieves a comma-separated string of the values for a single header.
*
* This method returns all of the header values of the given
* case-insensitive header name as a string concatenated together using
* a comma.
*
* NOTE: Not all header values may be appropriately represented using
* comma concatenation. For such headers, use getHeader() instead
* and supply your own delimiter when concatenating.
*
* If the header does not appear in the message, this method MUST return
* an empty string.
*
* @param string $name Case-insensitive header field name.
* @return string A string of values as provided for the given header
* concatenated together using a comma. If the header does not appear in
* the message, this method MUST return an empty string.
*/
public function getHeaderLine(string $name): string
{
// TODO: Implement getHeaderLine() method.
return $this->__call__(__FUNCTION__, $name);
}
/**
* Return an instance with the provided value replacing the specified header.
*
* While header names are case-insensitive, the casing of the header will
* be preserved by this function, and returned from getHeaders().
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new and/or updated header and value.
*
* @param string $name Case-insensitive header field name.
* @param string|string[] $value Header value(s).
* @return static
* @throws InvalidArgumentException for invalid header names or values.
*/
public function withHeader(string $name, $value): ResponseInterface
{
// TODO: Implement withHeader() method.
return $this->__call__(__FUNCTION__, $name, $value);
}
/**
* Return an instance with the specified header appended with the given value.
*
* Existing values for the specified header will be maintained. The new
* value(s) will be appended to the existing list. If the header did not
* exist previously, it will be added.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* new header and/or value.
*
* @param string $name Case-insensitive header field name to add.
* @param string|string[] $value Header value(s).
* @return static
* @throws InvalidArgumentException for invalid header names or values.
*/
public function withAddedHeader(string $name, $value): ResponseInterface
{
// TODO: Implement withAddedHeader() method.
return $this->__call__(__FUNCTION__, $name, $value);
}
/**
* Return an instance without the specified header.
*
* Header resolution MUST be done without case-sensitivity.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that removes
* the named header.
*
* @param string $name Case-insensitive header field name to remove.
* @return static
*/
public function withoutHeader(string $name): ResponseInterface
{
// TODO: Implement withoutHeader() method.
return $this->__call__(__FUNCTION__, $name);
}
/**
* Gets the body of the message.
*
* @return StreamInterface Returns the body as a stream.
*/
public function getBody(): StreamInterface
{
// TODO: Implement getBody() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specified message body.
*
* The body MUST be a StreamInterface object.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return a new instance that has the
* new body stream.
*
* @param StreamInterface $body Body.
* @return static
* @throws InvalidArgumentException When the body is not valid.
*/
public function withBody(StreamInterface $body): ResponseInterface
{
// TODO: Implement withBody() method.
return $this->__call__(__FUNCTION__, $body);
}
/**
* Gets the response status code.
*
* The status code is a 3-digit integer result code of the server's attempt
* to understand and satisfy the request.
*
* @return int Status code.
*/
public function getStatusCode(): int
{
// TODO: Implement getStatusCode() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specified status code and, optionally, reason phrase.
*
* If no reason phrase is specified, implementations MAY choose to default
* to the RFC 7231 or IANA recommended reason phrase for the response's
* status code.
*
* This method MUST be implemented in such a way as to retain the
* immutability of the message, and MUST return an instance that has the
* updated status and reason phrase.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @param int $code The 3-digit integer result code to set.
* @param string $reasonPhrase The reason phrase to use with the
* provided status code; if none is provided, implementations MAY
* use the defaults as suggested in the HTTP specification.
* @return static
* @throws InvalidArgumentException For invalid status code arguments.
*/
public function withStatus(int $code, string $reasonPhrase = ''): ResponseInterface
{
// TODO: Implement withStatus() method.
return $this->__call__(__FUNCTION__, $code, $reasonPhrase);
}
/**
* Gets the response reason phrase associated with the status code.
*
* Because a reason phrase is not a required element in a response
* status line, the reason phrase value MAY be null. Implementations MAY
* choose to return the default RFC 7231 recommended reason phrase (or those
* listed in the IANA HTTP Status Code Registry) for the response's
* status code.
*
* @link http://tools.ietf.org/html/rfc7231#section-6
* @link http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
* @return string Reason phrase; must return an empty string if none present.
*/
public function getReasonPhrase(): string
{
// TODO: Implement getReasonPhrase() method.
return $this->__call__(__FUNCTION__);
}
/**
* @param object $response
* @return void
*/
public function write(object $response): void
{
$response->end($this->getBody()->getContents());
}
}
+206
View File
@@ -2,7 +2,213 @@
namespace Kiri\Router;
use Closure;
use Exception;
use Kiri;
use Kiri\Annotation\Route\RequestMethod;
use ReflectionException;
const ROUTER_TYPE_HTTP = 'http';
/**
*
*
* $component->set([
* 'request' => [
* 'middlewares' => []
* ]
* ])
*/
class Router
{
const METHODS = [];
/**
* @var string
*/
private static string $type = ROUTER_TYPE_HTTP;
/**
* @param string $name
* @param Closure $closure
*/
public static function addServer(string $name, Closure $closure): void
{
static::$type = $name;
$closure();
static::$type = ROUTER_TYPE_HTTP;
}
/**
* @param Closure $handler
*/
public static function jsonp(Closure $handler): void
{
static::$type = 'json-rpc';
$handler();
static::$type = ROUTER_TYPE_HTTP;
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function post(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_POST], $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function get(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_GET], $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function options(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_OPTIONS], $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function any(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute(self::METHODS, $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function delete(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_DELETE], $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws ReflectionException
*/
public static function head(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_HEAD], $route, $handler);
}
/**
* @param string $route
* @param string|Closure $handler
* @throws
*/
public static function put(string $route, string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->addRoute([RequestMethod::REQUEST_PUT], $route, $handler);
}
/**
* @param array|RequestMethod $methods
* @param string $route
* @param array|string|Closure $handler
* @throws ReflectionException
*/
public static function addRoute(array|RequestMethod $methods, string $route, array|string|Closure $handler): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
if ($methods instanceof RequestMethod) {
$methods = [$methods];
}
$router->addRoute($methods, $route, $handler);
}
/**
* @param array $config
* @param Closure $closure
* @throws
*/
public static function group(array $config, Closure $closure): void
{
$router = Kiri::getDi()->get(DataGrip::class)->get(static::$type);
$router->groupTack[] = $config;
call_user_func($closure);
array_pop($router->groupTack);
}
/**
* @throws Exception
*/
public function scan_build_route(): void
{
scan_directory(APP_PATH, 'App');
$this->read_dir_file(APP_PATH . 'routes');
}
/**
* @param $path
* @return void
* @throws Exception
*/
private function read_dir_file($path): void
{
$files = glob($path . '/*');
for ($i = 0; $i < count($files); $i++) {
$file = $files[$i];
if (is_dir($file)) {
$this->read_dir_file($file);
} else {
$this->resolve_file($file);
}
}
}
/**
* @param $files
* @throws Exception
*/
private function resolve_file($files): void
{
try {
include "$files";
} catch (\Throwable $throwable) {
var_dump($throwable->getMessage());
}
}
}
+89 -273
View File
@@ -1,34 +1,20 @@
<?php
namespace Kiri\Message\Handler;
namespace Kiri\Router;
use Closure;
use Exception;
use Kiri;
use Kiri\Annotation\Inject;
use Kiri\Exception\ConfigException;
use Kiri\Message\Handler\Abstracts\MiddlewareManager;
use Kiri\Message\Handler\TreeHelper\HashTree;
use Kiri\Message\Handler\TreeHelper\MethodDelete;
use Kiri\Message\Handler\TreeHelper\MethodGet;
use Kiri\Message\Handler\TreeHelper\MethodHead;
use Kiri\Message\Handler\TreeHelper\MethodOptions;
use Kiri\Message\Handler\TreeHelper\MethodPut;
use Kiri\Message\Handler\TreeHelper\MethodPost;
use Kiri\Message\Handler\TreeHelper\TreeLeafInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Log\LoggerInterface;
use Kiri\Router\Base\NotFoundController;
use ReflectionException;
use Throwable;
use Traversable;
use Kiri\Router\Base\Middleware;
use Kiri\Core\HashMap;
use Kiri\Annotation\Route\RequestMethod;
const REQUEST_METHODS = [
'PUT' => new MethodPut()
];
/**
*
*/
@@ -39,41 +25,18 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate
private array $_item = [];
private array $reflex = [];
/**
* @var LoggerInterface
*/
#[Inject(LoggerInterface::class)]
public LoggerInterface $logger;
private array $globalMiddlewares = [];
public array $groupTack = [];
/**
* @var array
* @var HashMap
*/
private array $methods = [];
private HashMap $methods;
/**
*
*/
public function __construct()
{
$this->methods = [
RequestMethod::REQUEST_DELETE->getString() => di(MethodDelete::class),
RequestMethod::REQUEST_PUT->getString() => di(MethodPut::class),
RequestMethod::REQUEST_POST->getString() => di(MethodPost::class),
RequestMethod::REQUEST_OPTIONS->getString() => di(MethodOptions::class),
RequestMethod::REQUEST_GET->getString() => di(MethodGet::class),
RequestMethod::REQUEST_HEAD->getString() => di(MethodHead::class),
];
$this->methods = new HashMap();
}
@@ -86,178 +49,140 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate
}
/**
* @return array<MiddlewareInterface>
*/
public function getGlobalMiddlewares(): array
{
return $this->globalMiddlewares;
}
/**
* @param string $handler
* @return void
* @throws Exception
*/
public function addGlobalMiddlewares(string $handler): void
{
$handler = Kiri::getDi()->get($handler);
if (!in_array(MiddlewareInterface::class, class_implements($handler))) {
throw new Exception('The Middleware must instance ' . MiddlewareInterface::class);
}
$this->globalMiddlewares[] = $handler;
}
/**
* @param RequestMethod[] $method
* @param string $route
* @param string|Closure|array $closure
* @param string|Closure $closure
* @throws
*/
public function addRoute(array $method, string $route, string|Closure|array $closure)
public function addRoute(array $method, string $route, string|Closure $closure)
{
try {
$route = $this->_splicing_routing($route);
$middlewares = Kiri\Abstracts\Config::get('request.middlewares', []);
$interpreter = Kiri::getDi()->get(ControllerInterpreter::class);
if ($closure instanceof Closure) {
$middlewares = $this->_getRouteMiddlewares($middlewares);
} else if (is_string($closure)) {
$this->_route_analysis($closure);
$handler = $interpreter->addRouteByClosure($closure);
} else {
$handler = $this->resolve($closure, $interpreter);
}
foreach ($method as $value) {
$this->register($route, $value->getString(), $closure, $middlewares);
if ($value instanceof RequestMethod) {
$value = $value->getString();
}
$this->register($route, $value, $handler);
}
} catch (Throwable $throwable) {
$this->logger->error($throwable->getMessage(), [throwable($throwable)]);
error($throwable->getMessage(), [throwable($throwable)]);
}
}
/**
* @param string $closure
* @param ControllerInterpreter $interpreter
* @return Handler
* @throws ReflectionException
*/
private function resolve(string $closure, ControllerInterpreter $interpreter): Handler
{
[$className, $method] = explode('@', $closure);
$class = Kiri::getDi()->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 $closure
* @param $middlewares
* @param Handler $handler
* @return void
* @throws ReflectionException
*/
public function register(string $path, string $method, $closure, $middlewares): void
public function register(string $path, string $method, Handler $handler): void
{
$end = $this->methods[$method];
$json = str_split($path, 4);
$handler = new Handler($path, $closure, $middlewares);
foreach ($json as $item) {
/** @var TreeLeafInterface $leaf */
$leaf = new ($end::class)($item);
$leaf->setPath($item);
if (!$end->hasLeaf()) {
$end = $end->addLeaf($item, $leaf);
$hashMap = HashMap::Tree($this->methods, $method);
foreach (str_split($path, 4) as $item) {
if ($hashMap->has($item)) {
$hashMap = $hashMap->get($item);
} else {
$search = $end->searchLeaf($item);
if ($search == null) {
$end = $end->addLeaf($item, $leaf);
} else {
$end = $search;
$hashMap = new HashMap();
$hashMap->put($item, $hashMap);
}
}
$hashMap->put('handler', $handler);
$this->registerMiddleware($path);
}
/**
* @param string $path
* @return void
* @throws ReflectionException
* @throws Exception
*/
public function registerMiddleware(string $path): void
{
$middlewares = array_column($this->groupTack, 'middleware');
if (count($middlewares) > 0) {
$manager = Kiri::getDi()->get(Middleware::class);
foreach ($middlewares as $middleware) {
if (is_string($middleware)) {
$middleware = [$middleware];
}
foreach ($middleware as $value) {
$manager->addPathMiddleware($path, $value);
}
}
}
$end->setHandler($handler);
}
/**
* @param string $path
* @param string $method
* @return HashTree|null
* @throws ConfigException
* @throws ReflectionException
* @return Handler|null
*/
public function query(string $path, string $method): ?Handler
{
$parent = $this->methods[$method];
$string = str_split($path, 4);
foreach ($string as $item) {
$parent = $parent->searchLeaf($item);
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);
}
}
return $parent->getHandler();
return $parent->get('handler');
}
/**
* @param string $path
* @return Handler
* @throws ReflectionException
*/
private function NotFundHandler(string $path): Handler
{
$middlewares = Kiri\Abstracts\Config::get('request.middlewares', []);
return new Handler($path, [NotFoundController::class, 'fail'], $middlewares);
return new Handler([NotFoundController::class, 'fail'], []);
}
/**
* @param string $closure
* @throws Exception
*/
private function _route_analysis(string &$closure)
{
$closure = explode('@', $closure);
$closure[0] = $this->addNamespace($closure[0]);
if (class_exists($closure[0])) {
$this->_registerMiddleware($closure[0], $closure[1]);
}
}
/**
* @param array $middlewares
* @return array
* @throws Exception
*/
private function _getRouteMiddlewares(array $middlewares = []): array
{
$middleware = array_column($this->groupTack, 'middleware');
if (count($middleware = array_filter($middleware)) > 0) {
foreach ($middleware as $value) {
if (is_string($value)) {
$value = [$value];
}
foreach ($value as $item) {
if (!in_array(MiddlewareInterface::class, class_implements($item, true))) {
throw new Exception("middleware handler must instance MiddlewareInterface::class");
}
$middlewares[] = $item;
}
}
}
return $middlewares;
}
/**
* @param string $class
* @param string $method
* @return void
* @throws Exception
*/
private function _registerMiddleware(string $class, string $method): void
{
$middleware = $this->_getRouteMiddlewares();
foreach ($middleware as $value) {
MiddlewareManager::add($class, $method, $value);
}
}
/**
* @param string $route
* @return string
@@ -273,115 +198,6 @@ class RouterCollector implements \ArrayAccess, \IteratorAggregate
}
/**
* @param $closure
* @param $route
* @return array
* @throws ReflectionException
* @throws Exception
*/
protected function loadMiddlewares($closure, $route): array
{
$middlewares = [];
$close = new \ReflectionFunction($closure);
if (!empty($close->getClosureThis()) && env('environmental_workerId') == 0) {
$this->logger->warning('[' . $route . '] Static functions are recommended as callback functions.');
}
$middleware = array_column($this->groupTack, 'middleware');
$middleware = array_unique($middleware);
if (!empty($middleware = array_filter($middleware))) {
foreach ($middleware as $mi) {
if (!is_array($mi)) {
$mi = [$mi];
}
foreach ($mi as $item) {
if (!in_array(MiddlewareInterface::class, class_implements($item))) {
throw new Exception('The Middleware must instance ' . MiddlewareInterface::class);
}
$middlewares[$item] = $item;
}
}
$middlewares = array_values($middlewares);
}
return $middlewares;
}
/**
* @param $class
* @return string|null
*/
protected function addNamespace($class): ?string
{
$middleware = array_column($this->groupTack, 'namespace');
if (empty($middleware = array_filter($middleware))) {
return $class;
}
$middleware[] = $class;
return implode('\\', array_map(function ($value) {
return trim($value, '\\');
}, $middleware));
}
/**
* @param string $path
* @param string $method
* @return Handler|int|null
* @throws ReflectionException
*/
public function find(string $path, string $method): Handler|int|null
{
$dispatcher = match ($method) {
'OPTIONS' => $this->options($path, $method),
default => $this->other($path, $method)
};
if (is_null($dispatcher)) {
$middlewares = Kiri\Abstracts\Config::get('request.middlewares', []);
$dispatcher = new Handler($path, [NotFoundController::class, 'fail'], $middlewares);
} else if (is_integer($dispatcher)) {
$middlewares = Kiri\Abstracts\Config::get('request.middlewares', []);
$dispatcher = new Handler($path, [MethodErrorController::class, 'fail'], $middlewares);
}
return $dispatcher;
}
/**
* @param $path
* @param $method
* @return int|mixed
*/
public function other($path, $method): mixed
{
if (!isset($this->_item[$path])) {
return 404;
}
$handler = $this->_item[$path][$method] ?? null;
if (is_null($handler)) {
return 405;
}
return $handler;
}
/**
* @param $path
* @param $method
* @return int|mixed
*/
public function options($path, $method): mixed
{
$handler = $this->_item[$path][$method] ?? null;
if (is_null($handler)) {
return $this->_item['/*'][$method] ?? 405;
}
return $handler;
}
/**
* @param mixed $offset
* @return bool
+29 -64
View File
@@ -1,35 +1,25 @@
<?php
namespace Kiri\Message;
namespace Kiri\Router;
use Exception;
use Kiri;
use Kiri\Abstracts\AbstractServer;
use Kiri\Abstracts\Config;
use Kiri\Abstracts\CoordinatorManager;
use Kiri\Coordinator;
use Kiri\Di\ContainerInterface;
use Kiri\Di\Context;
use Kiri\Events\EventProvider;
use Kiri\Exception\ConfigException;
use Kiri\Message\Abstracts\ExceptionHandlerInterface;
use Kiri\Message\Abstracts\ResponseHelper;
use Kiri\Message\Constrict\RequestInterface;
use Kiri\Message\Constrict\ResponseInterface;
use Kiri\Message\Handler\DataGrip;
use Kiri\Message\Handler\Dispatcher;
use Kiri\Message\Handler\RouterCollector;
use Kiri\Message\Response as Psr7Response;
use Kiri\Server\Events\OnAfterWorkerStart;
use Kiri\Server\Events\OnBeforeWorkerStart;
use Kiri\Router\Interface\ExceptionHandlerInterface;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Http\Message\ResponseInterface as PsrResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Swoole\Http\Request;
use Swoole\Http\Response;
use Kiri\Router\Constrict\Response as KrcResponse;
use Kiri\Router\Constrict\Uri;
use Kiri\Router\Interface\OnRequestInterface;
use Kiri\Router\Base\ExceptionHandlerDispatcher;
/**
@@ -38,8 +28,6 @@ use Swoole\Http\Response;
class Server extends AbstractServer implements OnRequestInterface
{
use ResponseHelper;
public RouterCollector $router;
@@ -48,25 +36,21 @@ class Server extends AbstractServer implements OnRequestInterface
*/
public ExceptionHandlerInterface $exception;
private ContentType $contentType;
public Emitter $emitter;
/**
* @var HttpResponseEmitter
*/
public HttpResponseEmitter $emitter;
/**
* @param ContainerInterface $container
* @param Dispatcher $dispatcher
* @param EventProvider $provider
* @param DataGrip $dataGrip
* @param array $config
* @throws Exception
*/
public function __construct(
public ContainerInterface $container,
public Dispatcher $dispatcher,
public EventProvider $provider,
public DataGrip $dataGrip,
array $config = [])
{
parent::__construct($config);
@@ -80,37 +64,15 @@ class Server extends AbstractServer implements OnRequestInterface
*/
public function init()
{
$this->emitter = $this->container->get(Emitter::class);
$this->emitter = $this->container->get(HttpResponseEmitter::class);
$exception = Config::get('exception.http', ExceptionHandlerDispatcher::class);
$exception = Kiri::service()->get('request')->exception;
if (!in_array(ExceptionHandlerInterface::class, class_implements($exception))) {
$exception = ExceptionHandlerDispatcher::class;
}
$this->exception = $this->container->get($exception);
$this->provider->on(OnBeforeWorkerStart::class, [$this, 'onStartWaite']);
$this->provider->on(OnAfterWorkerStart::class, [$this, 'onEndWaite']);
$this->contentType = Config::get('response.format', ContentType::JSON);
$this->router = $this->dataGrip->get('http');
}
/**
* @return void
*/
public function onStartWaite(): void
{
CoordinatorManager::utility(Coordinator::WORKER_START)->waite();
}
/**
* @return void
*/
public function onEndWaite(): void
{
CoordinatorManager::utility(Coordinator::WORKER_START)->done();
$this->router = $this->container->get(DataGrip::class)->get(ROUTER_TYPE_HTTP);
}
@@ -127,30 +89,33 @@ class Server extends AbstractServer implements OnRequestInterface
$dispatcher = $this->router->query($request->server['request_uri'], $request->getMethod());
$PsrResponse = $dispatcher->dispatch->recover($PsrRequest);
$PsrResponse = (new HttpRequestHandler([], $dispatcher))->handle($PsrRequest);
} catch (\Throwable $throwable) {
$this->logger->error($throwable->getMessage(), [$throwable]);
$PsrResponse = $this->exception->emit($throwable, di(Constrict\Response::class));
} finally {
$this->emitter->sender($response, $PsrResponse);
$this->emitter->sender($PsrResponse, $response);
}
}
/**
* @param Request $request
* @return RequestInterface
* @throws Exception
*/
private function initRequestAndResponse(Request $request): \Psr\Http\Message\RequestInterface
private function initRequestAndResponse(Request $request): RequestInterface
{
/** @var ResponseInterface $PsrResponse */
$PsrResponse = Context::set(ResponseInterface::class, new Psr7Response());
$PsrResponse->withContentType($this->contentType);
/** @var \Kiri\Router\Response $response */
$response = Kiri::service()->get('response');
$serverRequest = (new ServerRequest())->withData($request->getContent())
->withServerParams($request->server)
->withServerTarget($request)
/** @var KrcResponse $PsrResponse */
$PsrResponse = Context::set(ResponseInterface::class, new KrcResponse());
$PsrResponse->withContentType($response->contentType);
$serverRequest = (new ServerRequest())->withHeaders($request->getData())
->withUri(Uri::parse($request))
->withProtocolVersion($request->server['server_protocol'])
->withCookieParams($request->cookie ?? [])
->withQueryParams($request->get)
->withUploadedFiles($request->files)
+45 -1
View File
@@ -3,6 +3,9 @@
namespace Kiri\Router;
use Kiri\Di\Context;
use Kiri\Message\Stream;
use Kiri\Router\Base\ExceptionHandlerDispatcher;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
@@ -11,10 +14,19 @@ use Psr\Http\Message\UriInterface;
/**
* @property-read bool $isPost
*/
class Request implements ServerRequestInterface
class ServerRequest implements ServerRequestInterface
{
public array $middlewares = [];
/**
* @var string
*/
public string $exception = ExceptionHandlerDispatcher::class;
/**
* @param string $method
* @param mixed ...$params
@@ -175,6 +187,38 @@ class Request implements ServerRequestInterface
return $this->__call__(__FUNCTION__, $name, $value);
}
/**
* @param string $data
* @return ServerRequestInterface
*/
public function withHeaders(string $data): ServerRequestInterface
{
$headers = explode("\r\n\r\n", $data);
$headers = explode("\r\n", $headers[0]);
foreach ($headers as $header) {
$keyValue = explode(': ', $header);
if (!isset($keyValue[1])) {
$keyValue[1] = '';
}
$keyValue[1] = explode(', ', $keyValue[1]);
$this->withHeader(...$keyValue);
}
return $this;
}
/**
* @param string $name
* @param string|null $default
* @return string|null
*/
public function header(string $name, ?string $default = null): ?string
{
return $this->__call__(__FUNCTION__, $name, $default);
}
/**
* Return an instance with the specified header appended with the given value.
*
+1 -1
View File
@@ -2,7 +2,7 @@
namespace Kiri\Router;
class ChunkResponse extends Response
class StreamResponse extends Response
{
public int $limit;
+5 -15
View File
@@ -1,11 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Exception;
use Kiri\Inject\Route\LocalService;
use ReflectionException;
use Kiri\Inject\Route\Container;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Email implements ValidatorInterface
@@ -22,19 +19,12 @@ class Email implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
$service = Container::getContext()->get(LocalService::class)->get('request');
if ($service->isPost) {
return filter_var($service->post($name, null), FILTER_VALIDATE_EMAIL);
} else {
return filter_var($service->query($name, null), FILTER_VALIDATE_EMAIL);
}
return filter_var($class->{$name}, FILTER_VALIDATE_EMAIL);
}
}
+5 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Ignore implements ValidatorInterface
@@ -8,12 +10,12 @@ class Ignore implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+6 -4
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class In implements ValidatorInterface
@@ -16,12 +18,12 @@ class In implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return in_array($class->{$name}, $this->value);
}
}
+6 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Length implements ValidatorInterface
@@ -16,12 +18,13 @@ class Length implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return mb_strlen($class->{$name}) === $this->value;
}
}
+5 -3
View File
@@ -1,7 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Max implements ValidatorInterface
@@ -17,12 +18,13 @@ class Max implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return $class->{$name} <= $this->value;
}
}
+6 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class MaxLength implements ValidatorInterface
@@ -16,12 +18,13 @@ class MaxLength implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return mb_strlen($class->{$name}) <= $this->value;
}
}
+5 -3
View File
@@ -1,7 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Min implements ValidatorInterface
@@ -17,12 +18,13 @@ class Min implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return $class->{$name} >= $this->value;
}
}
+6 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class MinLength implements ValidatorInterface
@@ -16,12 +18,13 @@ class MinLength implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return mb_strlen($class->{$name}) <= $this->value;
}
}
+6 -3
View File
@@ -1,8 +1,10 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Must implements ValidatorInterface
{
@@ -17,13 +19,14 @@ class Must implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return $class->{$name} === $this->value;
}
}
+6 -3
View File
@@ -1,18 +1,21 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NotEmpty implements ValidatorInterface
{
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return !empty($class->{$name});
}
}
+6 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
@@ -17,12 +19,13 @@ class NotIn implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return !in_array($class->{$name}, $this->value);
}
}
+6 -3
View File
@@ -1,6 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NotNull implements ValidatorInterface
@@ -8,12 +10,13 @@ class NotNull implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return $class->{$name} !== null;
}
}
+5 -16
View File
@@ -1,11 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Exception;
use Kiri\Inject\Route\LocalService;
use ReflectionException;
use Kiri\Inject\Route\Container;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Phone implements ValidatorInterface
@@ -13,20 +10,12 @@ class Phone implements ValidatorInterface
const REG = '/^1[356789]\d{9}$/';
/**
* @param object $class
* @param string $name
* @return bool
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
$service = Container::getContext()->get(LocalService::class)->get('request');
if ($service->isPost) {
$data = $service->post($name, null);
} else {
$data = $service->query($name, null);
}
return preg_match(self::REG, $data);
return preg_match(self::REG, $class->{$name});
}
}
+9 -3
View File
@@ -1,17 +1,23 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Required implements ValidatorInterface
{
public function dispatch(string $name): bool
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return true;
return $class->$name === null;
}
}
+5 -3
View File
@@ -1,7 +1,8 @@
<?php
namespace Kiri\Inject\Validator\Inject;
namespace Kiri\Router\Validator\Inject;
use Kiri\Router\Interface\ValidatorInterface;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Round implements ValidatorInterface
@@ -17,12 +18,13 @@ class Round implements ValidatorInterface
/**
* @param object $class
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
public function dispatch(object $class, string $name): bool
{
// TODO: Implement dispatch() method.
return round($name, $this->value) == $name;
return round($class->$name, $this->value) === $class->$name;
}
}
+31 -4
View File
@@ -1,8 +1,10 @@
<?php
namespace Kiri\Inject\Validator;
namespace Kiri\Router\Validator;
use Kiri\Inject\Validator\Inject\ValidatorInterface;
use Kiri\Router\Interface\ValidatorInterface;
use Kiri\Router\ServerRequest;
use Psr\Http\Message\ServerRequestInterface;
class Validator
{
@@ -17,14 +19,39 @@ class Validator
private string $message;
private object $formData;
/**
* @param string $name
* @param object $data
* @param ValidatorInterface $rule
* @return void
*/
public function addRule(string $name, ValidatorInterface $rule): void
public function addRule(string $name, object $data, ValidatorInterface $rule): void
{
$this->rules[$name] = $rule;
$this->formData = $data;
}
/**
* @param ServerRequestInterface|ServerRequest $request
* @return Validator
*/
public function bindData(ServerRequestInterface|ServerRequest $request): static
{
if ($request->isPost) {
$data = $request->getParsedBody();
} else {
$data = $request->getQueryParams();
}
foreach ($data as $key => $value) {
if (method_exists($this->formData, $key)) {
$this->formData->{$key} = $value;
}
}
return $this;
}
@@ -34,7 +61,7 @@ class Validator
public function run(): bool
{
foreach ($this->rules as $name => $rule) {
if (!$rule->dispatch($name)) {
if (!$rule->dispatch($this->formData, $name)) {
return false;
}
}
+35 -1
View File
@@ -2,7 +2,41 @@
namespace Kiri\Router\Validator;
class ValidatorMiddleware
use Kiri\Di\Context;
use Kiri\Router\Constrict\Stream;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
class ValidatorMiddleware implements MiddlewareInterface
{
/**
* @param Validator $validator
*/
public function __construct(readonly public Validator $validator)
{
}
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
try {
$this->validator->bindData($request);
return $handler->handle($request);
} catch (\Throwable $throwable) {
/** @var ResponseInterface $response */
$response = Context::get(ResponseInterface::class);
$response->withStatus(407)->withBody(new Stream($throwable->getMessage()));
return $response;
}
}
}