first commit

This commit is contained in:
2023-04-15 23:29:27 +08:00
commit 2d9ac93a7a
71 changed files with 4592 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
# kiri-router
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class ActionManager
{
}
+20
View File
@@ -0,0 +1,20 @@
<?php
namespace Kiri\Router;
/**
*
*/
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::TARGET_CLASS)]
class Aspect
{
/**
* @param array|string $actions
*/
public function __construct(readonly public array|string $actions)
{
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Kiri\Message\Aspect;
/**
*
*/
abstract class AbstractsAspect implements OnAspectInterface
{
}
+30
View File
@@ -0,0 +1,30 @@
<?php
namespace Kiri\Message\Aspect;
/**
*
*/
class JoinPoint implements OnJoinPointInterface
{
/**
* @param array|\Closure $handler
* @param mixed $params
*/
public function __construct(public array|\Closure $handler, public array $params)
{
}
/**
* @return mixed
*/
public function process(): mixed
{
return call_user_func($this->handler, ...$this->params);
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Kiri\Message\Aspect;
interface OnAspectInterface
{
/**
* @param OnJoinPointInterface $joinPoint
* @return mixed
*/
public function process(OnJoinPointInterface $joinPoint): mixed;
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Kiri\Message\Aspect;
interface OnJoinPointInterface
{
public function process();
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Kiri\Message\Aspect;
class TestAspect extends AbstractsAspect
{
/**
* @param OnJoinPointInterface $joinPoint
* @return mixed
*/
public function process(OnJoinPointInterface $joinPoint): mixed
{
$result = $joinPoint->process();
var_dump('111');
return $result;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Base;
class AbstractHandler
{
}
+40
View File
@@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Kiri\Message\Handler;
/**
* Interface AuthorizationInterface
* @package Kiri\Http
*/
interface AuthorizationInterface
{
/**
* @return string|int
* 获取唯一识别码
*/
public function getUniqueId(): string|int;
/**
* @param string $key
* @param int $timeout
* @return bool
*/
public function lock(string $key, int $timeout): bool;
/**
* @param string $key
* @return bool
*/
public function unlock(string $key): bool;
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Kiri\Message\Handler;
class ClosureController extends Controller
{
private \Closure $handler;
}
+36
View File
@@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace Kiri\Message\Handler;
use Kiri;
use Kiri\Message\Constrict\RequestInterface;
use Kiri\Message\Constrict\ResponseInterface;
use Kiri\Di\ContainerInterface;
use Psr\Log\LoggerInterface;
/**
* Class WebController
* @package Kiri\Web
*/
abstract class Controller
{
/**
* @param RequestInterface $request
* @param ResponseInterface $response
* @param LoggerInterface $logger
* @param ContainerInterface $container
*/
public function __construct(
public RequestInterface $request,
public ResponseInterface $response,
public LoggerInterface $logger,
public ContainerInterface $container)
{
}
}
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Kiri\Message\Handler;
use Exception;
use Kiri\Message\Handler\Abstracts\Middleware;
use Kiri\Message\ServerRequest;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
*
*/
class CoreMiddleware extends Middleware
{
/**
* @param ServerRequest $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
* @throws Exception
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$requestMethod = $request->getAccessControlRequestMethod();
$allowHeaders = $request->getAccessControlAllowHeaders();
$this->response->withAccessControlAllowOrigin('*')->withAccessControlRequestMethod($requestMethod)
->withAccessControlAllowHeaders($allowHeaders);
return $handler->handle($request);
}
}
+13
View File
@@ -0,0 +1,13 @@
<?php
namespace Kiri\Message\Abstracts;
use Kiri\Annotation\Inject;
use Kiri\Events\EventDispatch;
trait EventDispatchHelper
{
}
+34
View File
@@ -0,0 +1,34 @@
<?php
namespace Kiri\Message;
use Kiri\Message\Constrict\Response;
use Kiri\Message\Constrict\ResponseInterface;
use Kiri\Message\Abstracts\ExceptionHandlerInterface;
use Throwable;
/**
*
*/
class ExceptionHandlerDispatcher implements ExceptionHandlerInterface
{
/**
* @param Throwable $exception
* @param Response $response
* @return ResponseInterface
*/
public function emit(Throwable $exception, Response $response): ResponseInterface
{
$response->withContentType(ContentType::HTML);
if ($exception->getCode() == 404) {
return $response->withBody(new Stream($exception->getMessage()))->withStatus(404);
}
return $response->withBody(new Stream(jTraceEx($exception, null, true)))
->withStatus($exception->getCode() == 0 ? 500 : $exception->getCode());
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Kiri\Message\Handler;
use Kiri\Message\Constrict\ResponseInterface;
class MethodErrorController extends Controller
{
/**
* @return ResponseInterface
*/
public function fail(): ResponseInterface
{
return $this->response->failure(405, "method allow.");
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Base;
class Middleware
{
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace Kiri\Message\Handler;
use Kiri\Message\Constrict\ResponseInterface;
class NotFoundController extends Controller
{
/**
* @return ResponseInterface
*/
public function fail(): ResponseInterface
{
return $this->response->failure(404, "not found page.");
}
}
+212
View File
@@ -0,0 +1,212 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\StreamInterface;
class Message implements MessageInterface
{
/**
* 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()
{
// TODO: Implement getProtocolVersion() method.
}
/**
* 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)
{
// TODO: Implement withProtocolVersion() method.
}
/**
* 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()
{
// TODO: Implement getHeaders() method.
}
/**
* 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)
{
// TODO: Implement hasHeader() method.
}
/**
* 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)
{
// TODO: Implement getHeader() method.
}
/**
* 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)
{
// TODO: Implement getHeaderLine() method.
}
/**
* 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)
{
// TODO: Implement withHeader() method.
}
/**
* 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)
{
// TODO: Implement withAddedHeader() method.
}
/**
* 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)
{
// TODO: Implement withoutHeader() method.
}
/**
* Gets the body of the message.
*
* @return StreamInterface Returns the body as a stream.
*/
public function getBody()
{
// TODO: Implement getBody() method.
}
/**
* 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)
{
// TODO: Implement withBody() method.
}
}
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\UriInterface;
class Request extends Message implements RequestInterface
{
/**
* Retrieves the message's request target.
*
* Retrieves the message's request-target either as it will appear (for
* clients), as it appeared at request (for servers), or as it was
* specified for the instance (see withRequestTarget()).
*
* In most cases, this will be the origin-form of the composed URI,
* unless a value was provided to the concrete implementation (see
* withRequestTarget() below).
*
* If no URI is available, and no request-target has been specifically
* provided, this method MUST return the string "/".
*
* @return string
*/
public function getRequestTarget()
{
// TODO: Implement getRequestTarget() method.
}
/**
* Return an instance with the specific request-target.
*
* If the request needs a non-origin-form request-target — e.g., for
* specifying an absolute-form, authority-form, or asterisk-form —
* this method may be used to create an instance with the specified
* request-target, verbatim.
*
* 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
* changed request target.
*
* @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
* request-target forms allowed in request messages)
* @param string $requestTarget
* @return static
*/
public function withRequestTarget(string $requestTarget)
{
// TODO: Implement withRequestTarget() method.
}
/**
* Retrieves the HTTP method of the request.
*
* @return string Returns the request method.
*/
public function getMethod()
{
// TODO: Implement getMethod() method.
}
/**
* Return an instance with the provided HTTP method.
*
* While HTTP method names are typically all uppercase characters, HTTP
* method names are case-sensitive and thus implementations SHOULD NOT
* modify the given string.
*
* 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
* changed request method.
*
* @param string $method Case-sensitive method.
* @return static
* @throws \InvalidArgumentException for invalid HTTP methods.
*/
public function withMethod(string $method)
{
// TODO: Implement withMethod() method.
}
/**
* Retrieves the URI instance.
*
* This method MUST return a UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @return UriInterface Returns a UriInterface instance
* representing the URI of the request.
*/
public function getUri()
{
// TODO: Implement getUri() method.
}
/**
* Returns an instance with the provided URI.
*
* This method MUST update the Host header of the returned request by
* default if the URI contains a host component. If the URI does not
* contain a host component, any pre-existing Host header MUST be carried
* over to the returned request.
*
* You can opt-in to preserving the original state of the Host header by
* setting `$preserveHost` to `true`. When `$preserveHost` is set to
* `true`, this method interacts with the Host header in the following ways:
*
* - If the Host header is missing or empty, and the new URI contains
* a host component, this method MUST update the Host header in the returned
* request.
* - If the Host header is missing or empty, and the new URI does not contain a
* host component, this method MUST NOT update the Host header in the returned
* request.
* - If a Host header is present and non-empty, this method MUST NOT update
* the Host header in the returned request.
*
* 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 UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @param UriInterface $uri New request URI to use.
* @param bool $preserveHost Preserve the original state of the Host header.
* @return static
*/
public function withUri(UriInterface $uri, bool $preserveHost = false)
{
// TODO: Implement withUri() method.
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\ResponseInterface;
class Response extends Message implements ResponseInterface
{
/**
* 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()
{
// TODO: Implement getStatusCode() method.
}
/**
* 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 = '')
{
// TODO: Implement withStatus() method.
}
/**
* 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()
{
// TODO: Implement getReasonPhrase() method.
}
}
+263
View File
@@ -0,0 +1,263 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\ServerRequestInterface;
class ServerRequest extends Request implements ServerRequestInterface
{
/**
* Retrieve server parameters.
*
* Retrieves data related to the incoming request environment,
* typically derived from PHP's $_SERVER superglobal. The data IS NOT
* REQUIRED to originate from $_SERVER.
*
* @return array
*/
public function getServerParams()
{
// TODO: Implement getServerParams() method.
}
/**
* Retrieve cookies.
*
* Retrieves cookies sent by the client to the server.
*
* The data MUST be compatible with the structure of the $_COOKIE
* superglobal.
*
* @return array
*/
public function getCookieParams()
{
// TODO: Implement getCookieParams() method.
}
/**
* Return an instance with the specified cookies.
*
* The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
* be compatible with the structure of $_COOKIE. Typically, this data will
* be injected at instantiation.
*
* This method MUST NOT update the related Cookie header of the request
* instance, nor related values in the server params.
*
* 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 cookie values.
*
* @param array $cookies Array of key/value pairs representing cookies.
* @return static
*/
public function withCookieParams(array $cookies)
{
// TODO: Implement withCookieParams() method.
}
/**
* Retrieve query string arguments.
*
* Retrieves the deserialized query string arguments, if any.
*
* Note: the query params might not be in sync with the URI or server
* params. If you need to ensure you are only getting the original
* values, you may need to parse the query string from `getUri()->getQuery()`
* or from the `QUERY_STRING` server param.
*
* @return array
*/
public function getQueryParams()
{
// TODO: Implement getQueryParams() method.
}
/**
* Return an instance with the specified query string arguments.
*
* These values SHOULD remain immutable over the course of the incoming
* request. They MAY be injected during instantiation, such as from PHP's
* $_GET superglobal, or MAY be derived from some other value such as the
* URI. In cases where the arguments are parsed from the URI, the data
* MUST be compatible with what PHP's parse_str() would return for
* purposes of how duplicate query parameters are handled, and how nested
* sets are handled.
*
* Setting query string arguments MUST NOT change the URI stored by the
* request, nor the values in the server params.
*
* 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 query string arguments.
*
* @param array $query Array of query string arguments, typically from
* $_GET.
* @return static
*/
public function withQueryParams(array $query)
{
// TODO: Implement withQueryParams() method.
}
/**
* Retrieve normalized file upload data.
*
* This method returns upload metadata in a normalized tree, with each leaf
* an instance of Psr\Http\Message\UploadedFileInterface.
*
* These values MAY be prepared from $_FILES or the message body during
* instantiation, or MAY be injected via withUploadedFiles().
*
* @return array An array tree of UploadedFileInterface instances; an empty
* array MUST be returned if no data is present.
*/
public function getUploadedFiles()
{
// TODO: Implement getUploadedFiles() method.
}
/**
* Create a new instance with the specified uploaded files.
*
* 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 body parameters.
*
* @param array $uploadedFiles An array tree of UploadedFileInterface instances.
* @return static
* @throws \InvalidArgumentException if an invalid structure is provided.
*/
public function withUploadedFiles(array $uploadedFiles)
{
// TODO: Implement withUploadedFiles() method.
}
/**
* Retrieve any parameters provided in the request body.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, this method MUST
* return the contents of $_POST.
*
* Otherwise, this method may return any results of deserializing
* the request body content; as parsing returns structured content, the
* potential types MUST be arrays or objects only. A null value indicates
* the absence of body content.
*
* @return null|array|object The deserialized body parameters, if any.
* These will typically be an array or object.
*/
public function getParsedBody()
{
// TODO: Implement getParsedBody() method.
}
/**
* Return an instance with the specified body parameters.
*
* These MAY be injected during instantiation.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, use this method
* ONLY to inject the contents of $_POST.
*
* The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
* deserializing the request body content. Deserialization/parsing returns
* structured data, and, as such, this method ONLY accepts arrays or objects,
* or a null value if nothing was available to parse.
*
* As an example, if content negotiation determines that the request data
* is a JSON payload, this method could be used to create a request
* instance with the deserialized parameters.
*
* 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 body parameters.
*
* @param null|array|object $data The deserialized body data. This will
* typically be in an array or object.
* @return static
* @throws \InvalidArgumentException if an unsupported argument type is
* provided.
*/
public function withParsedBody($data)
{
// TODO: Implement withParsedBody() method.
}
/**
* Retrieve attributes derived from the request.
*
* The request "attributes" may be used to allow injection of any
* parameters derived from the request: e.g., the results of path
* match operations; the results of decrypting cookies; the results of
* deserializing non-form-encoded message bodies; etc. Attributes
* will be application and request specific, and CAN be mutable.
*
* @return array Attributes derived from the request.
*/
public function getAttributes()
{
// 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)
{
// TODO: Implement getAttribute() method.
}
/**
* Return an instance with the specified derived request attribute.
*
* This method allows setting a single derived request attribute as
* described in getAttributes().
*
* 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 attribute.
*
* @param string $name The attribute name.
* @param mixed $value The value of the attribute.
* @return static
* @see getAttributes()
*/
public function withAttribute(string $name, $value)
{
// TODO: Implement withAttribute() method.
}
/**
* Return an instance that removes the specified derived request attribute.
*
* This method allows removing a single derived request attribute as
* described in getAttributes().
*
* 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 attribute.
*
* @param string $name The attribute name.
* @return static
* @see getAttributes()
*/
public function withoutAttribute(string $name)
{
// TODO: Implement withoutAttribute() method.
}
}
+197
View File
@@ -0,0 +1,197 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\StreamInterface;
class Stream implements StreamInterface
{
/**
* Reads all data from the stream into a string, from the beginning to end.
*
* This method MUST attempt to seek to the beginning of the stream before
* reading data and read the stream until the end is reached.
*
* Warning: This could attempt to load a large amount of data into memory.
*
* This method MUST NOT raise an exception in order to conform with PHP's
* string casting operations.
*
* @see http://php.net/manual/en/language.oop5.magic.php#object.tostring
* @return string
*/
public function __toString()
{
// TODO: Implement __toString() method.
}
/**
* Closes the stream and any underlying resources.
*
* @return void
*/
public function close()
{
// TODO: Implement close() method.
}
/**
* Separates any underlying resources from the stream.
*
* After the stream has been detached, the stream is in an unusable state.
*
* @return resource|null Underlying PHP stream, if any
*/
public function detach()
{
// TODO: Implement detach() method.
}
/**
* Get the size of the stream if known.
*
* @return int|null Returns the size in bytes if known, or null if unknown.
*/
public function getSize()
{
// TODO: Implement getSize() method.
}
/**
* Returns the current position of the file read/write pointer
*
* @return int Position of the file pointer
* @throws \RuntimeException on error.
*/
public function tell()
{
// TODO: Implement tell() method.
}
/**
* Returns true if the stream is at the end of the stream.
*
* @return bool
*/
public function eof()
{
// TODO: Implement eof() method.
}
/**
* Returns whether or not the stream is seekable.
*
* @return bool
*/
public function isSeekable()
{
// TODO: Implement isSeekable() method.
}
/**
* Seek to a position in the stream.
*
* @link http://www.php.net/manual/en/function.fseek.php
* @param int $offset Stream offset
* @param int $whence Specifies how the cursor position will be calculated
* based on the seek offset. Valid values are identical to the built-in
* PHP $whence values for `fseek()`. SEEK_SET: Set position equal to
* offset bytes SEEK_CUR: Set position to current location plus offset
* SEEK_END: Set position to end-of-stream plus offset.
* @throws \RuntimeException on failure.
*/
public function seek(int $offset, int $whence = SEEK_SET)
{
// 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()
{
// TODO: Implement rewind() method.
}
/**
* Returns whether or not the stream is writable.
*
* @return bool
*/
public function isWritable()
{
// TODO: Implement isWritable() method.
}
/**
* Write data to the stream.
*
* @param string $string The string that is to be written.
* @return int Returns the number of bytes written to the stream.
* @throws \RuntimeException on failure.
*/
public function write(string $string)
{
// TODO: Implement write() method.
}
/**
* Returns whether or not the stream is readable.
*
* @return bool
*/
public function isReadable()
{
// TODO: Implement isReadable() method.
}
/**
* Read data from the stream.
*
* @param int $length Read up to $length bytes from the object and return
* them. Fewer than $length bytes may be returned if underlying stream
* call returns fewer bytes.
* @return string Returns the data read from the stream, or an empty string
* if no bytes are available.
* @throws \RuntimeException if an error occurs.
*/
public function read(int $length)
{
// TODO: Implement read() method.
}
/**
* Returns the remaining contents in a string
*
* @return string
* @throws \RuntimeException if unable to read or an error occurs while
* reading.
*/
public function getContents()
{
// TODO: Implement getContents() method.
}
/**
* Get stream metadata as an associative array or retrieve a specific key.
*
* The keys returned are identical to the keys returned from PHP's
* stream_get_meta_data() function.
*
* @link http://php.net/manual/en/function.stream-get-meta-data.php
* @param string|null $key Specific metadata to retrieve.
* @return array|mixed|null Returns an associative array if no key is
* 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)
{
// TODO: Implement getMetadata() method.
}
}
+355
View File
@@ -0,0 +1,355 @@
<?php
namespace Kiri\Router\Message;
use Psr\Http\Message\UriInterface;
class Uri implements UriInterface
{
/**
* Retrieve the scheme component of the URI.
*
* If no scheme is present, this method MUST return an empty string.
*
* The value returned MUST be normalized to lowercase, per RFC 3986
* Section 3.1.
*
* The trailing ":" character is not part of the scheme and MUST NOT be
* added.
*
* @see https://tools.ietf.org/html/rfc3986#section-3.1
* @return string The URI scheme.
*/
public function getScheme()
{
// TODO: Implement getScheme() method.
}
/**
* Retrieve the authority component of the URI.
*
* If no authority information is present, this method MUST return an empty
* string.
*
* The authority syntax of the URI is:
*
* <pre>
* [user-info@]host[:port]
* </pre>
*
* If the port component is not set or is the standard port for the current
* scheme, it SHOULD NOT be included.
*
* @see https://tools.ietf.org/html/rfc3986#section-3.2
* @return string The URI authority, in "[user-info@]host[:port]" format.
*/
public function getAuthority()
{
// TODO: Implement getAuthority() method.
}
/**
* Retrieve the user information component of the URI.
*
* If no user information is present, this method MUST return an empty
* string.
*
* If a user is present in the URI, this will return that value;
* additionally, if the password is also present, it will be appended to the
* user value, with a colon (":") separating the values.
*
* The trailing "@" character is not part of the user information and MUST
* NOT be added.
*
* @return string The URI user information, in "username[:password]" format.
*/
public function getUserInfo()
{
// TODO: Implement getUserInfo() method.
}
/**
* Retrieve the host component of the URI.
*
* If no host is present, this method MUST return an empty string.
*
* The value returned MUST be normalized to lowercase, per RFC 3986
* Section 3.2.2.
*
* @see http://tools.ietf.org/html/rfc3986#section-3.2.2
* @return string The URI host.
*/
public function getHost()
{
// TODO: Implement getHost() method.
}
/**
* Retrieve the port component of the URI.
*
* If a port is present, and it is non-standard for the current scheme,
* this method MUST return it as an integer. If the port is the standard port
* used with the current scheme, this method SHOULD return null.
*
* If no port is present, and no scheme is present, this method MUST return
* a null value.
*
* If no port is present, but a scheme is present, this method MAY return
* the standard port for that scheme, but SHOULD return null.
*
* @return null|int The URI port.
*/
public function getPort()
{
// TODO: Implement getPort() method.
}
/**
* Retrieve the path component of the URI.
*
* The path can either be empty or absolute (starting with a slash) or
* rootless (not starting with a slash). Implementations MUST support all
* three syntaxes.
*
* Normally, the empty path "" and absolute path "/" are considered equal as
* defined in RFC 7230 Section 2.7.3. But this method MUST NOT automatically
* do this normalization because in contexts with a trimmed base path, e.g.
* the front controller, this difference becomes significant. It's the task
* of the user to handle both "" and "/".
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.3.
*
* As an example, if the value should include a slash ("/") not intended as
* delimiter between path segments, that value MUST be passed in encoded
* form (e.g., "%2F") to the instance.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.3
* @return string The URI path.
*/
public function getPath()
{
// TODO: Implement getPath() method.
}
/**
* Retrieve the query string of the URI.
*
* If no query string is present, this method MUST return an empty string.
*
* The leading "?" character is not part of the query and MUST NOT be
* added.
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.4.
*
* As an example, if a value in a key/value pair of the query string should
* include an ampersand ("&") not intended as a delimiter between values,
* that value MUST be passed in encoded form (e.g., "%26") to the instance.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.4
* @return string The URI query string.
*/
public function getQuery()
{
// TODO: Implement getQuery() method.
}
/**
* Retrieve the fragment component of the URI.
*
* If no fragment is present, this method MUST return an empty string.
*
* The leading "#" character is not part of the fragment and MUST NOT be
* added.
*
* The value returned MUST be percent-encoded, but MUST NOT double-encode
* any characters. To determine what characters to encode, please refer to
* RFC 3986, Sections 2 and 3.5.
*
* @see https://tools.ietf.org/html/rfc3986#section-2
* @see https://tools.ietf.org/html/rfc3986#section-3.5
* @return string The URI fragment.
*/
public function getFragment()
{
// TODO: Implement getFragment() method.
}
/**
* Return an instance with the specified scheme.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified scheme.
*
* Implementations MUST support the schemes "http" and "https" case
* insensitively, and MAY accommodate other schemes if required.
*
* An empty scheme is equivalent to removing the scheme.
*
* @param string $scheme The scheme to use with the new instance.
* @return static A new instance with the specified scheme.
* @throws \InvalidArgumentException for invalid or unsupported schemes.
*/
public function withScheme(string $scheme)
{
// TODO: Implement withScheme() method.
}
/**
* Return an instance with the specified user information.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified user information.
*
* Password is optional, but the user information MUST include the
* user; an empty string for the user is equivalent to removing user
* information.
*
* @param string $user The user name to use for authority.
* @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)
{
// TODO: Implement withUserInfo() method.
}
/**
* Return an instance with the specified host.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified host.
*
* An empty host value is equivalent to removing the host.
*
* @param string $host The hostname to use with the new instance.
* @return static A new instance with the specified host.
* @throws \InvalidArgumentException for invalid hostnames.
*/
public function withHost(string $host)
{
// TODO: Implement withHost() method.
}
/**
* Return an instance with the specified port.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified port.
*
* Implementations MUST raise an exception for ports outside the
* established TCP and UDP port ranges.
*
* A null value provided for the port is equivalent to removing the port
* information.
*
* @param null|int $port The port to use with the new instance; a null value
* removes the port information.
* @return static A new instance with the specified port.
* @throws \InvalidArgumentException for invalid ports.
*/
public function withPort(?int $port)
{
// TODO: Implement withPort() method.
}
/**
* Return an instance with the specified path.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified path.
*
* The path can either be empty or absolute (starting with a slash) or
* rootless (not starting with a slash). Implementations MUST support all
* three syntaxes.
*
* If the path is intended to be domain-relative rather than path relative then
* it must begin with a slash ("/"). Paths not starting with a slash ("/")
* are assumed to be relative to some base path known to the application or
* consumer.
*
* Users can provide both encoded and decoded path characters.
* Implementations ensure the correct encoding as outlined in getPath().
*
* @param string $path The path to use with the new instance.
* @return static A new instance with the specified path.
* @throws \InvalidArgumentException for invalid paths.
*/
public function withPath(string $path)
{
// TODO: Implement withPath() method.
}
/**
* Return an instance with the specified query string.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified query string.
*
* Users can provide both encoded and decoded query characters.
* Implementations ensure the correct encoding as outlined in getQuery().
*
* An empty query string value is equivalent to removing the query string.
*
* @param string $query The query string to use with the new instance.
* @return static A new instance with the specified query string.
* @throws \InvalidArgumentException for invalid query strings.
*/
public function withQuery(string $query)
{
// TODO: Implement withQuery() method.
}
/**
* Return an instance with the specified URI fragment.
*
* This method MUST retain the state of the current instance, and return
* an instance that contains the specified URI fragment.
*
* Users can provide both encoded and decoded fragment characters.
* Implementations ensure the correct encoding as outlined in getFragment().
*
* An empty fragment value is equivalent to removing the fragment.
*
* @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)
{
// TODO: Implement withFragment() method.
}
/**
* Return the string representation as a URI reference.
*
* Depending on which components of the URI are present, the resulting
* string is either a full URI or relative reference according to RFC 3986,
* Section 4.1. The method concatenates the various components of the URI,
* using the appropriate delimiters:
*
* - If a scheme is present, it MUST be suffixed by ":".
* - If an authority is present, it MUST be prefixed by "//".
* - The path can be concatenated without delimiters. But there are two
* cases where the path has to be adjusted to make the URI reference
* valid as PHP does not allow to throw an exception in __toString():
* - If the path is rootless and an authority is present, the path MUST
* be prefixed by "/".
* - If the path is starting with more than one "/" and no authority is
* present, the starting slashes MUST be reduced to one.
* - If a query is present, it MUST be prefixed by "?".
* - If a fragment is present, it MUST be prefixed by "#".
*
* @see http://tools.ietf.org/html/rfc3986#section-4.1
* @return string
*/
public function __toString()
{
// TODO: Implement __toString() method.
}
}
+654
View File
@@ -0,0 +1,654 @@
<?php
namespace Kiri\Message;
enum ContentType
{
case X;
case OCTET_STREAM;
case PDF;
case AI;
case ATOM_XML;
case JS;
case EDI_X12;
case EDIFACT;
case JSON;
case JAVASCRIPT;
case OGG;
case RDF;
case RSS_XML;
case SOAP_XML;
case WOFF;
case XHTML_XML;
case XML;
case DTD;
case XOP_XML;
case ZIP;
case GZIP;
case XLS;
case X_001;
case X_301;
case X_906;
case A11;
case AWF;
case BMP;
case C4T;
case CAL;
case CDF;
case CEL;
case CG4;
case CIT;
case BOT;
case C90;
case CAT;
case CDR;
case CER;
case CGM;
case CMX;
case CRL;
case CSI;
case CUT;
case DBM;
case CMP;
case COT;
case CRT;
case DBF;
case DBX;
case DCX;
case DGN;
case DLL;
case DOT;
case DER;
case DIB;
case DOC;
case DRW;
case DWF;
case DXB;
case EDN;
case DWG;
case DXF;
case EMF;
case EPI;
case EPS;
case EXE;
case FDF;
case X_EPS;
case ETD;
case FIF;
case FRM;
case GBR;
case G4;
case GL2;
case HGL;
case HPG;
case HQX;
case HTA;
case GP4;
case HMR;
case HPL;
case HRF;
case ICB;
case ICO;
case IG4;
case III;
case INS;
case IFF;
case IGS;
case IMG;
case ISP;
case JPE;
case X_JAVASCRIPT;
case JPG;
case LAR;
case LATEX;
case LBM;
case LS;
case LTR;
case MAN;
case MDB;
case MAC;
case X_MDB;
case MFP;
case MI;
case MIL;
case MOCHA;
case MPD;
case MPP;
case MPT;
case MPW;
case MPX;
case MXP;
case NRF;
case OUT;
case P12;
case P7C;
case P7R;
case PC5;
case PCL;
case PDX;
case PGL;
case PKO;
case P10;
case P7B;
case P7M;
case P7S;
case PCI;
case PCX;
case PFX;
case PIC;
case PL;
case PLT;
case PNG;
case PPA;
case PPS;
case X_PPT;
case PRF;
case PRT;
case PS;
case PWZ;
case RA;
case RAS;
case POT;
case PPM;
case PPT;
case PR;
case PRN;
case X_PS;
case PTN;
case RED;
case RJS;
case RLC;
case RM;
case RAT;
case REC;
case RGB;
case RJT;
case RLE;
case RMF;
case RMJ;
case RMP;
case RMVB;
case RNX;
case RPM;
case RMS;
case RMX;
case RSML;
case RTF;
case RV;
case SAT;
case SDW;
case SLB;
case X_RTF;
case SAM;
case SDP;
case SIT;
case SLD;
case SMI;
case SMK;
case SMIL;
case SPC;
case SPL;
case SSM;
case STL;
case SST;
case TDF;
case TGA;
case STY;
case SWF;
case TG4;
case TIF;
case VDX;
case VPG;
case VSD;
case VST;
case VSW;
case VTX;
case TORRENT;
case VDA;
case VND_VISIO;
case VSS;
case X_VST;
case VSX;
case WB1;
case WB3;
case WIZ;
case WK4;
case WKS;
case WB2;
case WK3;
case WKQ;
case WMF;
case WMD;
case WP6;
case WPG;
case WQ1;
case WRI;
case WS;
case WMZ;
case WPD;
case WPL;
case WR1;
case WRK;
case WS2;
case XDP;
case XFD;
case XFDF;
case VND_MS_EXCEL;
case XWD;
case SIS;
case X_T;
case APK;
case X_B;
case SISX;
case IPA;
case XAP;
case XLW;
case XPL;
case ANV;
case UIN;
case H323;
case BIZ;
case CML;
case ASA;
case ASP;
case CSS;
case CSV;
case DCD;
case X_DTD;
case ENT;
case FO;
case HTC;
case HTML;
case HTX;
case HTM;
case HTT;
case JSP;
case MATH;
case MML;
case MTX;
case PLG;
case X_RDF;
case RT;
case SOL;
case SPP;
case STM;
case SVG;
case TLD;
case TXT;
case ULS;
case VML;
case TSD;
case VCF;
case VXML;
case WML;
case WSDL;
case WSC;
case XDR;
case XQL;
case XSD;
case XSLT;
case X_XML;
case XQ;
case XQUERY;
case XSL;
case XHTML;
case ODC;
case R3T;
case SOR;
case ACP;
case AIF;
case AIFF;
case AIFC;
case AU;
case LA1;
case LAVS;
case LMSFF;
case M3U;
case MIDI;
case MID;
case MP2;
case MP3;
case MP4;
case MND;
case MP1;
case MNS;
case MPGA;
case PLS;
case RAM;
case RMI;
case RMM;
case SND;
case WAV;
case WAX;
case WMA;
case ASF;
case ASX;
case AVI;
case IVF;
case M1V;
case M2V;
case M4E;
case MOVIE;
case MP2V;
case X_MP4;
case MPA;
case MPE;
case MPG;
case MPEG;
case MPS;
case MPV;
case MPV2;
case WM;
case WMV;
case WMX;
case WVX;
case TIFF;
case FAX;
case GIF;
case ICON;
case JFIF;
case X_JPE;
case JPEG;
case X_JPG;
case NET;
case X_PNG;
case RP;
case X_TIF;
case X_TIFF;
case WBMP;
case EML;
case MHT;
case MHTML;
case NWS;
case D_907;
case SLK;
case TOP;
case JAVA_CLASS;
case JAVA;
case X_DWF;
/**
* @param $method
* @return string
*/
public function toString(): string
{
return match ($this) {
self::X => 'application/x-',
self::OCTET_STREAM => 'application/octet-stream',
self::PDF => 'application/pdf',
self::AI, self::EPS, self::PS => 'application/postscript',
self::ATOM_XML => 'application/atom+xml',
self::JS => 'application/ecmascript',
self::EDI_X12 => 'application/EDI-X12',
self::EDIFACT => 'application/EDIFACT',
self::JSON => 'application/json; charset=utf-8',
self::JAVASCRIPT => 'application/javascript; charset=utf-8',
self::OGG => 'application/ogg',
self::RDF => 'application/rdf+xml',
self::RSS_XML => 'application/rss+xml',
self::SOAP_XML => 'application/soap+xml',
self::WOFF => 'application/font-woff',
self::XHTML_XML => 'application/xhtml+xml',
self::XML => 'application/xml; charset=utf-8',
self::DTD => 'application/xml-dtd',
self::XOP_XML => 'application/xop+xml',
self::ZIP => 'application/zip',
self::GZIP => 'application/gzip',
self::XLS => 'application/x-xls',
self::X_001 => 'application/x-001',
self::X_301 => 'application/x-301',
self::X_906 => 'application/x-906',
self::A11 => 'application/x-a11',
self::AWF => 'application/vnd.adobe.workflow',
self::BMP => 'application/x-bmp',
self::C4T => 'application/x-c4t',
self::CAL => 'application/x-cals',
self::CDF => 'application/x-netcdf',
self::CEL => 'application/x-cel',
self::CG4, self::G4, self::IG4 => 'application/x-g4',
self::CIT => 'application/x-cit',
self::BOT => 'application/x-bot',
self::C90 => 'application/x-c90',
self::CAT => 'application/vnd.ms-pki.seccat',
self::CDR => 'application/x-cdr',
self::CER, self::CRT, self::DER => 'application/x-x509-ca-cert',
self::CGM => 'application/x-cgm',
self::CMX => 'application/x-cmx',
self::CRL => 'application/pkix-crl',
self::CSI => 'application/x-csi',
self::CUT => 'application/x-cut',
self::DBM => 'application/x-dbm',
self::CMP => 'application/x-cmp',
self::COT => 'application/x-cot',
self::DBF => 'application/x-dbf',
self::DBX => 'application/x-dbx',
self::DCX => 'application/x-dcx',
self::DGN => 'application/x-dgn',
self::DLL, self::EXE => 'application/x-msdownload',
self::DOT, self::DOC, self::RTF, self::WIZ => 'application/msword',
self::DIB => 'application/x-dib',
self::DRW => 'application/x-drw',
self::DWF => 'application/x-dwf',
self::DXB => 'application/x-dxb',
self::EDN => 'application/vnd.adobe.edn',
self::DWG => 'application/x-dwg',
self::DXF => 'application/x-dxf',
self::EMF => 'application/x-emf',
self::EPI => 'application/x-epi',
self::FDF => 'application/vnd.fdf',
self::X_EPS, self::X_PS => 'application/x-ps',
self::ETD => 'application/x-ebx',
self::FIF => 'application/fractals',
self::FRM => 'application/x-frm',
self::GBR => 'application/x-gbr',
self::GL2 => 'application/x-gl2',
self::HGL => 'application/x-hgl',
self::HPG => 'application/x-hpgl',
self::HQX => 'application/mac-binhex40',
self::HTA => 'application/hta',
self::GP4 => 'application/x-gp4',
self::HMR => 'application/x-hmr',
self::HPL => 'application/x-hpl',
self::HRF => 'application/x-hrf',
self::ICB => 'application/x-icb',
self::ICO => 'application/x-ico',
self::III => 'application/x-iphone',
self::INS, self::ISP => 'application/x-internet-signup',
self::IFF => 'application/x-iff',
self::IGS => 'application/x-igs',
self::IMG => 'application/x-img',
self::JPE => 'application/x-jpe',
self::X_JAVASCRIPT, self::LS, self::MOCHA => 'application/x-javascript',
self::JPG => 'application/x-jpg',
self::LAR => 'application/x-laplayer-reg',
self::LATEX => 'application/x-latex',
self::LBM => 'application/x-lbm',
self::LTR => 'application/x-ltr',
self::MAN => 'application/x-troff-man',
self::MDB => 'application/msaccess',
self::MAC => 'application/x-mac',
self::X_MDB => 'application/x-mdb',
self::MFP, self::SWF => 'application/x-shockwave-flash',
self::MI => 'application/x-mi',
self::MIL => 'application/x-mil',
self::MPD, self::MPP, self::MPT, self::MPW, self::MPX => 'application/vnd.ms-project',
self::MXP => 'application/x-mmxp',
self::NRF => 'application/x-nrf',
self::OUT => 'application/x-out',
self::P12, self::PFX => 'application/x-pkcs12',
self::P7C, self::P7M => 'application/pkcs7-mime',
self::P7R => 'application/x-pkcs7-certreqresp',
self::PC5 => 'application/x-pc5',
self::PCL => 'application/x-pcl',
self::PDX => 'application/vnd.adobe.pdx',
self::PGL => 'application/x-pgl',
self::PKO => 'application/vnd.ms-pki.pko',
self::P10 => 'application/pkcs10',
self::P7B, self::SPC => 'application/x-pkcs7-certificates',
self::P7S => 'application/pkcs7-signature',
self::PCI => 'application/x-pci',
self::PCX => 'application/x-pcx',
self::PIC => 'application/x-pic',
self::PL => 'application/x-perl',
self::PLT => 'application/x-plt',
self::PNG => 'application/x-png',
self::PPA, self::PPS, self::PWZ, self::POT, self::PPT => 'application/vnd.ms-powerpoint',
self::X_PPT => 'application/x-ppt',
self::PRF => 'application/pics-rules',
self::PRT => 'application/x-prt',
self::RA => 'audio/vnd.rn-realaudio',
self::RAS => 'application/x-ras',
self::PPM => 'application/x-ppm',
self::PR => 'application/x-pr',
self::PRN => 'application/x-prn',
self::PTN => 'application/x-ptn',
self::RED => 'application/x-red',
self::RJS => 'application/vnd.rn-realsystem-rjs',
self::RLC => 'application/x-rlc',
self::RM => 'application/vnd.rn-realmedia',
self::RAT => 'application/rat-file',
self::REC => 'application/vnd.rn-recording',
self::RGB => 'application/x-rgb',
self::RJT => 'application/vnd.rn-realsystem-rjt',
self::RLE => 'application/x-rle',
self::RMF => 'application/vnd.adobe.rmf',
self::RMJ => 'application/vnd.rn-realsystem-rmj',
self::RMP => 'application/vnd.rn-rn_music_package',
self::RMVB => 'application/vnd.rn-realmedia-vbr',
self::RNX => 'application/vnd.rn-realplayer',
self::RPM => 'audio/x-pn-realaudio-plugin',
self::RMS => 'application/vnd.rn-realmedia-secure',
self::RMX => 'application/vnd.rn-realsystem-rmx',
self::RSML => 'application/vnd.rn-rsml',
self::RV => 'video/vnd.rn-realvideo',
self::SAT => 'application/x-sat',
self::SDW => 'application/x-sdw',
self::SLB => 'application/x-slb',
self::X_RTF => 'application/x-rtf',
self::SAM => 'application/x-sam',
self::SDP => 'application/sdp',
self::SIT => 'application/x-stuffit',
self::SLD => 'application/x-sld',
self::SMI, self::SMIL => 'application/smil',
self::SMK => 'application/x-smk',
self::SPL => 'application/futuresplash',
self::SSM => 'application/streamingmedia',
self::STL => 'application/vnd.ms-pki.stl',
self::SST => 'application/vnd.ms-pki.certstore',
self::TDF => 'application/x-tdf',
self::TGA => 'application/x-tga',
self::STY => 'application/x-sty',
self::TG4 => 'application/x-tg4',
self::TIF => 'application/x-tif',
self::VDX, self::VST, self::VSW, self::VTX, self::VND_VISIO, self::VSS, self::VSX => 'application/vnd.visio',
self::VPG => 'application/x-vpeg005',
self::VSD => 'application/x-vsd',
self::TORRENT => 'application/x-bittorrent',
self::VDA => 'application/x-vda',
self::X_VST => 'application/x-vst',
self::WB1 => 'application/x-wb1',
self::WB3 => 'application/x-wb3',
self::WK4 => 'application/x-wk4',
self::WKS => 'application/x-wks',
self::WB2 => 'application/x-wb2',
self::WK3 => 'application/x-wk3',
self::WKQ => 'application/x-wkq',
self::WMF => 'application/x-wmf',
self::WMD => 'application/x-ms-wmd',
self::WP6 => 'application/x-wp6',
self::WPG => 'application/x-wpg',
self::WQ1 => 'application/x-wq1',
self::WRI => 'application/x-wri',
self::WS, self::WS2 => 'application/x-ws',
self::WMZ => 'application/x-ms-wmz',
self::WPD => 'application/x-wpd',
self::WPL => 'application/vnd.ms-wpl',
self::WR1 => 'application/x-wr1',
self::WRK => 'application/x-wrk',
self::XDP => 'application/vnd.adobe.xdp',
self::XFD => 'application/vnd.adobe.xfd',
self::XFDF => 'application/vnd.adobe.xfdf',
self::VND_MS_EXCEL => 'application/vnd.ms-excel',
self::XWD => 'application/x-xwd',
self::SIS, self::SISX => 'application/vnd.symbian.install',
self::X_T => 'application/x-x_t',
self::APK => 'application/vnd.android.package-archive',
self::X_B => 'application/x-x_b',
self::IPA => 'application/vnd.iphone',
self::XAP => 'application/x-silverlight-app',
self::XLW => 'application/x-xlw',
self::XPL, self::PLS => 'audio/scpls',
self::ANV => 'application/x-anv',
self::UIN => 'application/x-icq',
self::H323 => 'text/h323',
self::BIZ, self::CML, self::DCD, self::X_DTD, self::FO, self::ENT, self::MATH, self::MML, self::MTX, self::X_RDF, self::SPP, self::SVG, self::TLD, self::VML, self::TSD, self::VXML, self::WSDL, self::XDR, self::XSD, self::XQL, self::XSLT, self::X_XML, self::XQ, self::XQUERY, self::XSL => 'text/xml; charset=utf-8',
self::ASA => 'text/asa',
self::ASP => 'text/asp',
self::CSS => 'text/css',
self::CSV => 'text/csv',
self::HTC => 'text/x-component',
self::HTML, self::STM, self::HTX, self::HTM, self::JSP, self::PLG, self::XHTML => 'text/html; charset=utf-8',
self::HTT => 'text/webviewhtml',
self::RT => 'text/vnd.rn-realtext',
self::SOL, self::SOR => 'text/plain',
self::TXT => 'text/plain纯文字内容',
self::ULS => 'text/iuls',
self::VCF => 'text/x-vcard',
self::WML => 'text/vnd.wap.wml',
self::WSC => 'text/scriptlet',
self::ODC => 'text/x-ms-odc',
self::R3T => 'text/vnd.rn-realtext3d',
self::ACP => 'audio/x-mei-aac',
self::AIF, self::AIFF, self::AIFC => 'audio/aiff',
self::AU, self::SND => 'audio/basic',
self::LA1 => 'audio/x-liquid-file',
self::LAVS => 'audio/x-liquid-secure',
self::LMSFF => 'audio/x-la-lms',
self::M3U => 'audio/mpegurl',
self::MIDI, self::MID, self::RMI => 'audio/mid',
self::MP2 => 'audio/mp2',
self::MP3 => 'audio/mp3',
self::MP4 => 'audio/mp4',
self::MND => 'audio/x-musicnet-download',
self::MP1 => 'audio/mp1',
self::MNS => 'audio/x-musicnet-stream',
self::MPGA => 'audio/rn-mpeg',
self::RAM, self::RMM => 'audio/x-pn-realaudio',
self::WAV => 'audio/wav',
self::WAX => 'audio/x-ms-wax',
self::WMA => 'audio/x-ms-wma',
self::ASF, self::ASX => 'video/x-ms-asf',
self::AVI => 'video/avi',
self::IVF => 'video/x-ivf',
self::M1V, self::M2V, self::MPE, self::MPS => 'video/x-mpeg',
self::M4E, self::X_MP4 => 'video/mpeg4',
self::MOVIE => 'video/x-sgi-movie',
self::MP2V, self::MPV2 => 'video/mpeg',
self::MPA => 'video/x-mpg',
self::MPG, self::MPEG, self::MPV => 'video/mpg',
self::WM => 'video/x-ms-wm',
self::WMV => 'video/x-ms-wmv',
self::WMX => 'video/x-ms-wmx',
self::WVX => 'video/x-ms-wvx',
self::TIFF, self::X_TIF, self::X_TIFF => 'image/tiff',
self::FAX => 'image/fax',
self::GIF => 'image/gif',
self::ICON => 'image/x-icon',
self::JFIF, self::X_JPE, self::JPEG, self::X_JPG => 'image/jpeg',
self::NET => 'image/pnetvue',
self::X_PNG => 'image/png',
self::RP => 'image/vnd.rn-realpix',
self::WBMP => 'image/vnd.wap.wbmp',
self::EML, self::MHT, self::MHTML, self::NWS => 'message/rfc822',
self::D_907 => 'drawing/907',
self::SLK => 'drawing/x-slk',
self::TOP => 'drawing/x-top',
self::JAVA_CLASS, self::JAVA => 'java/*',
self::X_DWF => 'Model/vnd.dwf'
};
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
namespace Kiri\Router\Inject;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
class ControllerInterpreter
{
/**
* @param string $className
* @param string|ReflectionMethod $method
* @param ReflectionClass|null $reflection
* @return void
* @throws ReflectionException
*/
public function addRouteByString(string $className, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): void
{
$class = \Kiri::getDi()->get($className);
if (is_null($reflection)) {
$reflection = \Kiri::getDi()->getReflect($className);
}
$this->resolveMethod($class, $method, $reflection);
}
/**
* @param object $class
* @param string|ReflectionMethod $method
* @param ReflectionClass|null $reflection
* @return void
* @throws ReflectionException
*/
public function addRouteByObject(object $class, string|ReflectionMethod $method, ?ReflectionClass $reflection = null): void
{
if (is_null($reflection)) {
$reflection = \Kiri::getDi()->getReflect($class::class);
}
$this->resolveMethod($class, $method, $reflection);
}
/**
* @param object $class
* @param string|ReflectionMethod $reflectionMethod
* @param ReflectionClass $reflectionClass
* @return void
* @throws ReflectionException
*/
public function resolveMethod(object $class, string|\ReflectionMethod $reflectionMethod, ReflectionClass $reflectionClass): void
{
if (is_string($reflectionMethod)) {
$reflectionMethod = $reflectionClass->getMethod($reflectionMethod);
}
$this->resolveProperties($reflectionClass, $class);
$parameters = $this->resolveMethodParams($reflectionMethod->getParameters());
$handler = new Handler([$reflectionClass->getName(), $reflectionMethod->getName()], $parameters);
ActionManager::add($reflectionClass->getName(), $reflectionMethod->getName(), $handler);
}
/**
* @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;
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Kiri\Message\Handler;
use Kiri;
class DataGrip
{
private array $servers = [];
/**
* @param $type
* @return RouterCollector
*/
public function get($type): RouterCollector
{
if (!isset($this->servers[$type])) {
$this->servers[$type] = Kiri::getDi()->create(RouterCollector::class);
}
return $this->servers[$type];
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class Handler
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class HttpRequestHandler
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class HttpResponseEmitter
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Inject\Route;
abstract class AbstractRequestMethod
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Inject;
class Aspect
{
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Kiri\Inject\Route;
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);
}
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_DELETE, $this->path, [$class, $method]);
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Kiri\Inject\Route;
#[\Attribute(\Attribute::TARGET_METHOD)]
class Filter
{
public function __construct()
{
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_GET, $this->path, [$class, $method]);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_HEAD, $this->path, [$class, $method]);
}
}
+14
View File
@@ -0,0 +1,14 @@
<?php
namespace Kiri\Inject\Route;
#[\Attribute(\Attribute::TARGET_METHOD)]
class Interceptor
{
public function __construct()
{
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Inject\InjectPropertyInterface;
use Kiri\Message\Handler\Abstracts\MiddlewareManager;
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
class Middleware implements InjectPropertyInterface
{
/**
* @param string $middleware
*/
public function __construct(readonly public string $middleware)
{
}
/**
* @param object $class
* @param string $property
* @return void
*/
public function dispatch(object $class, string $property): void
{
// TODO: Implement dispatch() method.
MiddlewareManager::add($class::class, $property, $this->middleware);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_OPTIONS, $this->path, [$class, $method]);
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Inject;
class Other
{
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_POST, $this->path, [$class, $method]);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Kiri\Inject\Route;
use Kiri\Annotation\Route\RequestMethod;
use Kiri\Message\Handler\Router;
#[\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
*/
public function dispatch(object $class, string $method): void
{
// TODO: Implement dispatch() method.
Router::addRoute(RequestMethod::REQUEST_PUT, $this->path, [$class, $method]);
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Kiri\Inject\Route;
#[\Attribute(\Attribute::TARGET_PARAMETER)]
class QueryData
{
/**
* @param array $rule
*/
public function __construct(readonly public array $rule)
{
}
}
@@ -0,0 +1,23 @@
<?php
namespace Kiri\Message\Abstracts;
use Kiri\Message\Constrict\Response;
use Throwable;
use Kiri\Message\Constrict\ResponseInterface;
/**
*
*/
interface ExceptionHandlerInterface
{
/**
* @param Throwable $exception
* @param Response $response
* @return ResponseInterface
*/
public function emit(Throwable $exception, Response $response): ResponseInterface;
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace Kiri\Router;
interface InjectRouteInterface
{
/**
* @param object $class
* @param string $method
* @return void
*/
public function dispatch(object $class, string $method): void;
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Interface;
interface OnRequestInterface
{
}
+11
View File
@@ -0,0 +1,11 @@
<?php
namespace Kiri\Router\Validator\Inject;
interface ValidatorInterface
{
public function dispatch(object $class, string $name): bool;
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class Response
{
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router;
class Router
{
}
+429
View File
@@ -0,0 +1,429 @@
<?php
namespace Kiri\Message\Handler;
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 ReflectionException;
use Throwable;
use Traversable;
use Kiri\Annotation\Route\RequestMethod;
const REQUEST_METHODS = [
'PUT' => new MethodPut()
];
/**
*
*/
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
*/
private array $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),
];
}
/**
* @return Traversable
*/
public function getIterator(): Traversable
{
return new \ArrayIterator($this->_item);
}
/**
* @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
* @throws
*/
public function addRoute(array $method, string $route, string|Closure|array $closure)
{
try {
$route = $this->_splicing_routing($route);
$middlewares = Kiri\Abstracts\Config::get('request.middlewares', []);
if ($closure instanceof Closure) {
$middlewares = $this->_getRouteMiddlewares($middlewares);
} else if (is_string($closure)) {
$this->_route_analysis($closure);
}
foreach ($method as $value) {
$this->register($route, $value->getString(), $closure, $middlewares);
}
} catch (Throwable $throwable) {
$this->logger->error($throwable->getMessage(), [throwable($throwable)]);
}
}
/**
* @param string $path
* @param string $method
* @param $closure
* @param $middlewares
* @return void
* @throws ReflectionException
*/
public function register(string $path, string $method, $closure, $middlewares): 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);
} else {
$search = $end->searchLeaf($item);
if ($search == null) {
$end = $end->addLeaf($item, $leaf);
} else {
$end = $search;
}
}
}
$end->setHandler($handler);
}
/**
* @param string $path
* @param string $method
* @return HashTree|null
* @throws ConfigException
* @throws ReflectionException
*/
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 ($parent === null) {
return $this->NotFundHandler($path);
}
}
return $parent->getHandler();
}
/**
* @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);
}
/**
* @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
*/
protected function _splicing_routing(string $route): string
{
$route = ltrim($route, '/');
$prefix = array_column($this->groupTack, 'prefix');
if (empty($prefix = array_filter($prefix))) {
return '/' . $route;
}
return '/' . implode('/', $prefix) . '/' . $route;
}
/**
* @param $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
*/
public function offsetExists(mixed $offset): bool
{
// TODO: Implement offsetExists() method.
return isset($this->_item[$offset]);
}
/**
* @param mixed $offset
* @return Router|null
*/
public function offsetGet(mixed $offset): ?Router
{
if ($this->offsetExists($offset)) {
return $this->_item[$offset];
}
return null;
}
/**
* @param mixed $offset
* @param mixed $value
* @return void
*/
public function offsetSet(mixed $offset, mixed $value): void
{
// TODO: Implement offsetSet() method.
$this->_item[$offset] = $value;
}
/**
* @param mixed $offset
* @return void
*/
public function offsetUnset(mixed $offset): void
{
unset($this->_item[$offset]);
}
}
+165
View File
@@ -0,0 +1,165 @@
<?php
namespace Kiri\Message;
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 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 Swoole\Http\Request;
use Swoole\Http\Response;
/**
*
*/
class Server extends AbstractServer implements OnRequestInterface
{
use ResponseHelper;
public RouterCollector $router;
/**
* @var ExceptionHandlerInterface
*/
public ExceptionHandlerInterface $exception;
private ContentType $contentType;
public Emitter $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);
}
/**
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws Exception
*/
public function init()
{
$this->emitter = $this->container->get(Emitter::class);
$exception = Config::get('exception.http', ExceptionHandlerDispatcher::class);
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();
}
/**
* @param Request $request
* @param Response $response
* @throws Exception
*/
public function onRequest(Request $request, Response $response): void
{
try {
/** @var ServerRequest $PsrRequest */
$PsrRequest = $this->initRequestAndResponse($request);
$dispatcher = $this->router->query($request->server['request_uri'], $request->getMethod());
$PsrResponse = $dispatcher->dispatch->recover($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);
}
}
/**
* @param Request $request
* @return RequestInterface
* @throws Exception
*/
private function initRequestAndResponse(Request $request): \Psr\Http\Message\RequestInterface
{
/** @var ResponseInterface $PsrResponse */
$PsrResponse = Context::set(ResponseInterface::class, new Psr7Response());
$PsrResponse->withContentType($this->contentType);
$serverRequest = (new ServerRequest())->withData($request->getContent())
->withServerParams($request->server)
->withServerTarget($request)
->withCookieParams($request->cookie ?? [])
->withQueryParams($request->get)
->withUploadedFiles($request->files)
->withMethod($request->getMethod())
->withParsedBody($request->post);
/** @var ServerRequest $PsrRequest */
return Context::set(RequestInterface::class, $serverRequest);
}
}
+680
View File
@@ -0,0 +1,680 @@
<?php
namespace Kiri\Router;
use Kiri\Di\Context;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
/**
* @property-read bool $isPost
*/
class Request implements ServerRequestInterface
{
/**
* @param string $method
* @param mixed ...$params
* @return mixed
*/
private function __call__(string $method, ...$params): mixed
{
if (!Context::exists(ServerRequestInterface::class)) {
$response = Context::set(ServerRequestInterface::class, new static());
} else {
$response = Context::get(ServerRequestInterface::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): ServerRequestInterface
{
// 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): ServerRequestInterface
{
// 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): ServerRequestInterface
{
// 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): ServerRequestInterface
{
// 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): ServerRequestInterface
{
// TODO: Implement withBody() method.
return $this->__call__(__FUNCTION__, $body);
}
/**
* Retrieves the message's request target.
*
* Retrieves the message's request-target either as it will appear (for
* clients), as it appeared at request (for servers), or as it was
* specified for the instance (see withRequestTarget()).
*
* In most cases, this will be the origin-form of the composed URI,
* unless a value was provided to the concrete implementation (see
* withRequestTarget() below).
*
* If no URI is available, and no request-target has been specifically
* provided, this method MUST return the string "/".
*
* @return string
*/
public function getRequestTarget(): string
{
// TODO: Implement getRequestTarget() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specific request-target.
*
* If the request needs a non-origin-form request-target — e.g., for
* specifying an absolute-form, authority-form, or asterisk-form —
* this method may be used to create an instance with the specified
* request-target, verbatim.
*
* 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
* changed request target.
*
* @link http://tools.ietf.org/html/rfc7230#section-5.3 (for the various
* request-target forms allowed in request messages)
* @param string $requestTarget
* @return static
*/
public function withRequestTarget(string $requestTarget): ServerRequestInterface
{
// TODO: Implement withRequestTarget() method.
return $this->__call__(__FUNCTION__, $requestTarget);
}
/**
* Retrieves the HTTP method of the request.
*
* @return string Returns the request method.
*/
public function getMethod(): string
{
// TODO: Implement getMethod() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the provided HTTP method.
*
* While HTTP method names are typically all uppercase characters, HTTP
* method names are case-sensitive and thus implementations SHOULD NOT
* modify the given string.
*
* 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
* changed request method.
*
* @param string $method Case-sensitive method.
* @return static
* @throws \InvalidArgumentException for invalid HTTP methods.
*/
public function withMethod(string $method): ServerRequestInterface
{
// TODO: Implement withMethod() method.
return $this->__call__(__FUNCTION__, $method);
}
/**
* Retrieves the URI instance.
*
* This method MUST return a UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @return UriInterface Returns a UriInterface instance
* representing the URI of the request.
*/
public function getUri(): UriInterface
{
// TODO: Implement getUri() method.
return $this->__call__(__FUNCTION__);
}
/**
* Returns an instance with the provided URI.
*
* This method MUST update the Host header of the returned request by
* default if the URI contains a host component. If the URI does not
* contain a host component, any pre-existing Host header MUST be carried
* over to the returned request.
*
* You can opt-in to preserving the original state of the Host header by
* setting `$preserveHost` to `true`. When `$preserveHost` is set to
* `true`, this method interacts with the Host header in the following ways:
*
* - If the Host header is missing or empty, and the new URI contains
* a host component, this method MUST update the Host header in the returned
* request.
* - If the Host header is missing or empty, and the new URI does not contain a
* host component, this method MUST NOT update the Host header in the returned
* request.
* - If a Host header is present and non-empty, this method MUST NOT update
* the Host header in the returned request.
*
* 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 UriInterface instance.
*
* @link http://tools.ietf.org/html/rfc3986#section-4.3
* @param UriInterface $uri New request URI to use.
* @param bool $preserveHost Preserve the original state of the Host header.
* @return static
*/
public function withUri(UriInterface $uri, bool $preserveHost = false): ServerRequestInterface
{
// TODO: Implement withUri() method.
return $this->__call__(__FUNCTION__, $uri, $preserveHost);
}
/**
* Retrieve server parameters.
*
* Retrieves data related to the incoming request environment,
* typically derived from PHP's $_SERVER superglobal. The data IS NOT
* REQUIRED to originate from $_SERVER.
*
* @return array
*/
public function getServerParams(): array
{
// TODO: Implement getServerParams() method.
return $this->__call__(__FUNCTION__);
}
/**
* Retrieve cookies.
*
* Retrieves cookies sent by the client to the server.
*
* The data MUST be compatible with the structure of the $_COOKIE
* superglobal.
*
* @return array
*/
public function getCookieParams(): array
{
// TODO: Implement getCookieParams() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specified cookies.
*
* The data IS NOT REQUIRED to come from the $_COOKIE superglobal, but MUST
* be compatible with the structure of $_COOKIE. Typically, this data will
* be injected at instantiation.
*
* This method MUST NOT update the related Cookie header of the request
* instance, nor related values in the server params.
*
* 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 cookie values.
*
* @param array $cookies Array of key/value pairs representing cookies.
* @return static
*/
public function withCookieParams(array $cookies): ServerRequestInterface
{
// TODO: Implement withCookieParams() method.
return $this->__call__(__FUNCTION__, $cookies);
}
/**
* Retrieve query string arguments.
*
* Retrieves the deserialized query string arguments, if any.
*
* Note: the query params might not be in sync with the URI or server
* params. If you need to ensure you are only getting the original
* values, you may need to parse the query string from `getUri()->getQuery()`
* or from the `QUERY_STRING` server param.
*
* @return array
*/
public function getQueryParams(): array
{
// TODO: Implement getQueryParams() method.
return $this->__call__(__FUNCTION__);
}
/**
* Return an instance with the specified query string arguments.
*
* These values SHOULD remain immutable over the course of the incoming
* request. They MAY be injected during instantiation, such as from PHP's
* $_GET superglobal, or MAY be derived from some other value such as the
* URI. In cases where the arguments are parsed from the URI, the data
* MUST be compatible with what PHP's parse_str() would return for
* purposes of how duplicate query parameters are handled, and how nested
* sets are handled.
*
* Setting query string arguments MUST NOT change the URI stored by the
* request, nor the values in the server params.
*
* 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 query string arguments.
*
* @param array $query Array of query string arguments, typically from
* $_GET.
* @return static
*/
public function withQueryParams(array $query): ServerRequestInterface
{
// TODO: Implement withQueryParams() method.
return $this->__call__(__FUNCTION__, $query);
}
/**
* Retrieve normalized file upload data.
*
* This method returns upload metadata in a normalized tree, with each leaf
* an instance of Psr\Http\Message\UploadedFileInterface.
*
* These values MAY be prepared from $_FILES or the message body during
* instantiation, or MAY be injected via withUploadedFiles().
*
* @return array An array tree of UploadedFileInterface instances; an empty
* array MUST be returned if no data is present.
*/
public function getUploadedFiles(): array
{
// TODO: Implement getUploadedFiles() method.
return $this->__call__(__FUNCTION__);
}
/**
* Create a new instance with the specified uploaded files.
*
* 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 body parameters.
*
* @param array $uploadedFiles An array tree of UploadedFileInterface instances.
* @return static
* @throws \InvalidArgumentException if an invalid structure is provided.
*/
public function withUploadedFiles(array $uploadedFiles): ServerRequestInterface
{
// TODO: Implement withUploadedFiles() method.
return $this->__call__(__FUNCTION__, $uploadedFiles);
}
/**
* Retrieve any parameters provided in the request body.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, this method MUST
* return the contents of $_POST.
*
* Otherwise, this method may return any results of deserializing
* the request body content; as parsing returns structured content, the
* potential types MUST be arrays or objects only. A null value indicates
* the absence of body content.
*
* @return null|array|object The deserialized body parameters, if any.
* These will typically be an array or object.
*/
public function getParsedBody(): object|array|null
{
// TODO: Implement getParsedBody() method.
return $this->__call__(__FUNCTION__);
}
/**
* @param string $name
* @param mixed|null $default
* @return mixed
*/
public function post(string $name, mixed $default = null): mixed
{
$parseBody = $this->getParsedBody();
if (is_array($parseBody)) {
return $parseBody[$name] ?? $default;
}
return $parseBody ?? $default;
}
/**
* @return bool
*/
public function getIsPost(): bool
{
return $this->getMethod() == 'POST';
}
/**
* @param string $name
* @param mixed $default
* @return mixed
*/
public function query(string $name, mixed $default = null): mixed
{
return $this->getQueryParams()[$name] ?? $default;
}
/**
* Return an instance with the specified body parameters.
*
* These MAY be injected during instantiation.
*
* If the request Content-Type is either application/x-www-form-urlencoded
* or multipart/form-data, and the request method is POST, use this method
* ONLY to inject the contents of $_POST.
*
* The data IS NOT REQUIRED to come from $_POST, but MUST be the results of
* deserializing the request body content. Deserialization/parsing returns
* structured data, and, as such, this method ONLY accepts arrays or objects,
* or a null value if nothing was available to parse.
*
* As an example, if content negotiation determines that the request data
* is a JSON payload, this method could be used to create a request
* instance with the deserialized parameters.
*
* 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 body parameters.
*
* @param null|array|object $data The deserialized body data. This will
* typically be in an array or object.
* @return static
* @throws \InvalidArgumentException if an unsupported argument type is
* provided.
*/
public function withParsedBody($data): ServerRequestInterface
{
// TODO: Implement withParsedBody() method.
return $this->__call__(__FUNCTION__, $data);
}
/**
* Retrieve attributes derived from the request.
*
* The request "attributes" may be used to allow injection of any
* parameters derived from the request: e.g., the results of path
* match operations; the results of decrypting cookies; the results of
* deserializing non-form-encoded message bodies; etc. Attributes
* will be application and request specific, and CAN be mutable.
*
* @return array Attributes derived from the request.
*/
public function getAttributes(): array
{
// TODO: Implement getAttributes() method.
return $this->__call__(__FUNCTION__);
}
/**
* 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 $this->__call__(__FUNCTION__, $name, $default);
}
/**
* Return an instance with the specified derived request attribute.
*
* This method allows setting a single derived request attribute as
* described in getAttributes().
*
* 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 attribute.
*
* @param string $name The attribute name.
* @param mixed $value The value of the attribute.
* @return static
* @see getAttributes()
*/
public function withAttribute(string $name, $value): ServerRequestInterface
{
// TODO: Implement withAttribute() method.
return $this->__call__(__FUNCTION__, $name, $value);
}
/**
* Return an instance that removes the specified derived request attribute.
*
* This method allows removing a single derived request attribute as
* described in getAttributes().
*
* 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 attribute.
*
* @param string $name The attribute name.
* @return static
* @see getAttributes()
*/
public function withoutAttribute(string $name): ServerRequestInterface
{
// TODO: Implement withoutAttribute() method.
return $this->__call__(__FUNCTION__, $name);
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Kiri\Router;
class ChunkResponse extends Response
{
public int $limit;
/**
* @param object $response
* @return void
*/
public function write(object $response): void
{
$body = $this->getBody();
$total = ceil($this->limit / $body->getSize());
for ($i = 0; $i < $total; $i++) {
$body->seek($i);
$response->write($body->read($this->limit));
}
$response->end();
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Kiri\Inject\Validator\Inject;
use Exception;
use Kiri\Inject\Route\LocalService;
use ReflectionException;
use Kiri\Inject\Route\Container;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Email implements ValidatorInterface
{
/**
* @return string
*/
public function getError(): string
{
return '';
}
/**
* @param string $name
* @return bool
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(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);
}
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Ignore implements ValidatorInterface
{
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class In implements ValidatorInterface
{
/**
* @param array $value
*/
public function __construct(readonly public array $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Length implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Max implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class MaxLength implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Min implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class MinLength implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Must implements ValidatorInterface
{
/**
* @param mixed $value
*/
public function __construct(readonly public mixed $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+18
View File
@@ -0,0 +1,18 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NotEmpty implements ValidatorInterface
{
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NotIn implements ValidatorInterface
{
/**
* @param array $value
*/
public function __construct(readonly public array $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+19
View File
@@ -0,0 +1,19 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class NotNull implements ValidatorInterface
{
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Kiri\Inject\Validator\Inject;
use Exception;
use Kiri\Inject\Route\LocalService;
use ReflectionException;
use Kiri\Inject\Route\Container;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Phone implements ValidatorInterface
{
const REG = '/^1[356789]\d{9}$/';
/**
* @param string $name
* @return bool
* @throws ReflectionException
* @throws Exception
*/
public function dispatch(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);
}
}
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Required implements ValidatorInterface
{
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return true;
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kiri\Inject\Validator\Inject;
#[\Attribute(\Attribute::TARGET_PROPERTY)]
class Round implements ValidatorInterface
{
/**
* @param int $value
*/
public function __construct(readonly public int $value)
{
}
/**
* @param string $name
* @return bool
*/
public function dispatch(string $name): bool
{
// TODO: Implement dispatch() method.
return round($name, $this->value) == $name;
}
}
+65
View File
@@ -0,0 +1,65 @@
<?php
namespace Kiri\Inject\Validator;
use Kiri\Inject\Validator\Inject\ValidatorInterface;
class Validator
{
/**
* @var ValidatorInterface[]
*/
private array $rules = [];
private string $message;
/**
* @param string $name
* @param ValidatorInterface $rule
* @return void
*/
public function addRule(string $name, ValidatorInterface $rule): void
{
$this->rules[$name] = $rule;
}
/**
* @return bool
*/
public function run(): bool
{
foreach ($this->rules as $name => $rule) {
if (!$rule->dispatch($name)) {
return false;
}
}
return true;
}
/**
* @param $field
* @return bool
*/
private function addError($field): bool
{
$this->message = $field . ' error';
return false;
}
/**
* @return string
*/
public function error(): string
{
return $this->message;
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Kiri\Router\Validator;
class ValidatorMiddleware
{
}