first commit
This commit is contained in:
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class ActionManager
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Message\Aspect;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class AbstractsAspect implements OnAspectInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Message\Aspect;
|
||||||
|
|
||||||
|
interface OnAspectInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param OnJoinPointInterface $joinPoint
|
||||||
|
* @return mixed
|
||||||
|
*/
|
||||||
|
public function process(OnJoinPointInterface $joinPoint): mixed;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Message\Aspect;
|
||||||
|
|
||||||
|
interface OnJoinPointInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function process();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Base;
|
||||||
|
|
||||||
|
class AbstractHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Message\Handler;
|
||||||
|
|
||||||
|
class ClosureController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
private \Closure $handler;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Message\Abstracts;
|
||||||
|
|
||||||
|
use Kiri\Annotation\Inject;
|
||||||
|
use Kiri\Events\EventDispatch;
|
||||||
|
|
||||||
|
trait EventDispatchHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Base;
|
||||||
|
|
||||||
|
class Middleware
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class Handler
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class HttpRequestHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class HttpResponseEmitter
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Inject\Route;
|
||||||
|
|
||||||
|
abstract class AbstractRequestMethod
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Inject;
|
||||||
|
|
||||||
|
class Aspect
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Inject\Route;
|
||||||
|
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||||
|
class Filter
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Inject\Route;
|
||||||
|
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||||
|
class Interceptor
|
||||||
|
{
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Inject;
|
||||||
|
|
||||||
|
class Other
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Interface;
|
||||||
|
|
||||||
|
interface OnRequestInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Validator\Inject;
|
||||||
|
|
||||||
|
interface ValidatorInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public function dispatch(object $class, string $name): bool;
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class Response
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router;
|
||||||
|
|
||||||
|
class Router
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Kiri\Router\Validator;
|
||||||
|
|
||||||
|
class ValidatorMiddleware
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user