From 2d9ac93a7aca0f4b5a742d30519cd928ebd41b4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=91=E6=9E=97?= Date: Sat, 15 Apr 2023 23:29:27 +0800 Subject: [PATCH] first commit --- README.md | 1 + src/ActionManager.php | 8 + src/Aspect.php | 20 + src/Aspect/AbstractsAspect.php | 13 + src/Aspect/JoinPoint.php | 30 + src/Aspect/OnAspectInterface.php | 16 + src/Aspect/OnJoinPointInterface.php | 11 + src/Aspect/TestAspect.php | 25 + src/Base/AbstractHandler.php | 8 + src/Base/AuthorizationInterface.php | 40 ++ src/Base/ClosureController.php | 11 + src/Base/Controller.php | 36 ++ src/Base/CoreMiddleware.php | 37 ++ src/Base/EventDispatchHelper.php | 13 + src/Base/ExceptionHandlerDispatcher.php | 34 + src/Base/MethodErrorController.php | 18 + src/Base/Middleware.php | 8 + src/Base/NotFoundController.php | 19 + src/Constrict/Message.php | 212 ++++++ src/Constrict/Request.php | 132 ++++ src/Constrict/Response.php | 65 ++ src/Constrict/ServerRequest.php | 263 ++++++++ src/Constrict/Stream.php | 197 ++++++ src/Constrict/Uri.php | 355 ++++++++++ src/ContentType.php | 654 +++++++++++++++++++ src/ControllerInterpreter.php | 126 ++++ src/DataGrip.php | 26 + src/Handler.php | 8 + src/HttpRequestHandler.php | 8 + src/HttpResponseEmitter.php | 8 + src/Inject/AbstractRequestMethod.php | 8 + src/Inject/Aspect.php | 8 + src/Inject/Controller.php | 49 ++ src/Inject/Delete.php | 33 + src/Inject/Filter.php | 14 + src/Inject/Get.php | 33 + src/Inject/Head.php | 33 + src/Inject/Interceptor.php | 14 + src/Inject/Middleware.php | 32 + src/Inject/Options.php | 33 + src/Inject/Other.php | 8 + src/Inject/Post.php | 33 + src/Inject/Put.php | 33 + src/Inject/QueryData.php | 17 + src/Interface/ExceptionHandlerInterface.php | 23 + src/Interface/InjectRouteInterface.php | 16 + src/Interface/OnRequestInterface.php | 8 + src/Interface/ValidatorInterface.php | 11 + src/Response.php | 8 + src/Router.php | 8 + src/RouterCollector.php | 429 ++++++++++++ src/Server.php | 165 +++++ src/ServerRequest.php | 680 ++++++++++++++++++++ src/StreamResponse.php | 27 + src/Validator/Inject/Email.php | 40 ++ src/Validator/Inject/Ignore.php | 19 + src/Validator/Inject/In.php | 27 + src/Validator/Inject/Length.php | 27 + src/Validator/Inject/Max.php | 28 + src/Validator/Inject/MaxLength.php | 27 + src/Validator/Inject/Min.php | 28 + src/Validator/Inject/MinLength.php | 27 + src/Validator/Inject/Must.php | 29 + src/Validator/Inject/NotEmpty.php | 18 + src/Validator/Inject/NotIn.php | 28 + src/Validator/Inject/NotNull.php | 19 + src/Validator/Inject/Phone.php | 32 + src/Validator/Inject/Required.php | 17 + src/Validator/Inject/Round.php | 28 + src/Validator/Validator.php | 65 ++ src/Validator/ValidatorMiddleware.php | 8 + 71 files changed, 4592 insertions(+) create mode 100644 README.md create mode 100644 src/ActionManager.php create mode 100644 src/Aspect.php create mode 100644 src/Aspect/AbstractsAspect.php create mode 100644 src/Aspect/JoinPoint.php create mode 100644 src/Aspect/OnAspectInterface.php create mode 100644 src/Aspect/OnJoinPointInterface.php create mode 100644 src/Aspect/TestAspect.php create mode 100644 src/Base/AbstractHandler.php create mode 100644 src/Base/AuthorizationInterface.php create mode 100644 src/Base/ClosureController.php create mode 100644 src/Base/Controller.php create mode 100644 src/Base/CoreMiddleware.php create mode 100644 src/Base/EventDispatchHelper.php create mode 100644 src/Base/ExceptionHandlerDispatcher.php create mode 100644 src/Base/MethodErrorController.php create mode 100644 src/Base/Middleware.php create mode 100644 src/Base/NotFoundController.php create mode 100644 src/Constrict/Message.php create mode 100644 src/Constrict/Request.php create mode 100644 src/Constrict/Response.php create mode 100644 src/Constrict/ServerRequest.php create mode 100644 src/Constrict/Stream.php create mode 100644 src/Constrict/Uri.php create mode 100644 src/ContentType.php create mode 100644 src/ControllerInterpreter.php create mode 100644 src/DataGrip.php create mode 100644 src/Handler.php create mode 100644 src/HttpRequestHandler.php create mode 100644 src/HttpResponseEmitter.php create mode 100644 src/Inject/AbstractRequestMethod.php create mode 100644 src/Inject/Aspect.php create mode 100644 src/Inject/Controller.php create mode 100644 src/Inject/Delete.php create mode 100644 src/Inject/Filter.php create mode 100644 src/Inject/Get.php create mode 100644 src/Inject/Head.php create mode 100644 src/Inject/Interceptor.php create mode 100644 src/Inject/Middleware.php create mode 100644 src/Inject/Options.php create mode 100644 src/Inject/Other.php create mode 100644 src/Inject/Post.php create mode 100644 src/Inject/Put.php create mode 100644 src/Inject/QueryData.php create mode 100644 src/Interface/ExceptionHandlerInterface.php create mode 100644 src/Interface/InjectRouteInterface.php create mode 100644 src/Interface/OnRequestInterface.php create mode 100644 src/Interface/ValidatorInterface.php create mode 100644 src/Response.php create mode 100644 src/Router.php create mode 100644 src/RouterCollector.php create mode 100644 src/Server.php create mode 100644 src/ServerRequest.php create mode 100644 src/StreamResponse.php create mode 100644 src/Validator/Inject/Email.php create mode 100644 src/Validator/Inject/Ignore.php create mode 100644 src/Validator/Inject/In.php create mode 100644 src/Validator/Inject/Length.php create mode 100644 src/Validator/Inject/Max.php create mode 100644 src/Validator/Inject/MaxLength.php create mode 100644 src/Validator/Inject/Min.php create mode 100644 src/Validator/Inject/MinLength.php create mode 100644 src/Validator/Inject/Must.php create mode 100644 src/Validator/Inject/NotEmpty.php create mode 100644 src/Validator/Inject/NotIn.php create mode 100644 src/Validator/Inject/NotNull.php create mode 100644 src/Validator/Inject/Phone.php create mode 100644 src/Validator/Inject/Required.php create mode 100644 src/Validator/Inject/Round.php create mode 100644 src/Validator/Validator.php create mode 100644 src/Validator/ValidatorMiddleware.php diff --git a/README.md b/README.md new file mode 100644 index 0000000..411278c --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# kiri-router diff --git a/src/ActionManager.php b/src/ActionManager.php new file mode 100644 index 0000000..dc0c021 --- /dev/null +++ b/src/ActionManager.php @@ -0,0 +1,8 @@ +handler, ...$this->params); + } + +} diff --git a/src/Aspect/OnAspectInterface.php b/src/Aspect/OnAspectInterface.php new file mode 100644 index 0000000..094103b --- /dev/null +++ b/src/Aspect/OnAspectInterface.php @@ -0,0 +1,16 @@ +process(); + + var_dump('111'); + + return $result; + } + +} diff --git a/src/Base/AbstractHandler.php b/src/Base/AbstractHandler.php new file mode 100644 index 0000000..de33424 --- /dev/null +++ b/src/Base/AbstractHandler.php @@ -0,0 +1,8 @@ +getAccessControlRequestMethod(); + $allowHeaders = $request->getAccessControlAllowHeaders(); + + $this->response->withAccessControlAllowOrigin('*')->withAccessControlRequestMethod($requestMethod) + ->withAccessControlAllowHeaders($allowHeaders); + + return $handler->handle($request); + } + +} diff --git a/src/Base/EventDispatchHelper.php b/src/Base/EventDispatchHelper.php new file mode 100644 index 0000000..db5f980 --- /dev/null +++ b/src/Base/EventDispatchHelper.php @@ -0,0 +1,13 @@ +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()); + } + +} diff --git a/src/Base/MethodErrorController.php b/src/Base/MethodErrorController.php new file mode 100644 index 0000000..f8d60e5 --- /dev/null +++ b/src/Base/MethodErrorController.php @@ -0,0 +1,18 @@ +response->failure(405, "method allow."); + } +} diff --git a/src/Base/Middleware.php b/src/Base/Middleware.php new file mode 100644 index 0000000..34bf5e9 --- /dev/null +++ b/src/Base/Middleware.php @@ -0,0 +1,8 @@ +response->failure(404, "not found page."); + } + +} diff --git a/src/Constrict/Message.php b/src/Constrict/Message.php new file mode 100644 index 0000000..c294a39 --- /dev/null +++ b/src/Constrict/Message.php @@ -0,0 +1,212 @@ +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. + } +} diff --git a/src/Constrict/Request.php b/src/Constrict/Request.php new file mode 100644 index 0000000..8f3ce37 --- /dev/null +++ b/src/Constrict/Request.php @@ -0,0 +1,132 @@ +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. + } +} diff --git a/src/Constrict/Stream.php b/src/Constrict/Stream.php new file mode 100644 index 0000000..0b59381 --- /dev/null +++ b/src/Constrict/Stream.php @@ -0,0 +1,197 @@ + + * [user-info@]host[:port] + * + * + * 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. + } +} diff --git a/src/ContentType.php b/src/ContentType.php new file mode 100644 index 0000000..4ec093d --- /dev/null +++ b/src/ContentType.php @@ -0,0 +1,654 @@ + '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' + }; + } +} diff --git a/src/ControllerInterpreter.php b/src/ControllerInterpreter.php new file mode 100644 index 0000000..e9e5f1c --- /dev/null +++ b/src/ControllerInterpreter.php @@ -0,0 +1,126 @@ +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; + } + + +} diff --git a/src/DataGrip.php b/src/DataGrip.php new file mode 100644 index 0000000..4a34df1 --- /dev/null +++ b/src/DataGrip.php @@ -0,0 +1,26 @@ +servers[$type])) { + $this->servers[$type] = Kiri::getDi()->create(RouterCollector::class); + } + return $this->servers[$type]; + } + + +} diff --git a/src/Handler.php b/src/Handler.php new file mode 100644 index 0000000..82f5d04 --- /dev/null +++ b/src/Handler.php @@ -0,0 +1,8 @@ +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); + } + } + +} diff --git a/src/Inject/Delete.php b/src/Inject/Delete.php new file mode 100644 index 0000000..0913c50 --- /dev/null +++ b/src/Inject/Delete.php @@ -0,0 +1,33 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/Filter.php b/src/Inject/Filter.php new file mode 100644 index 0000000..a2fdc18 --- /dev/null +++ b/src/Inject/Filter.php @@ -0,0 +1,14 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/Head.php b/src/Inject/Head.php new file mode 100644 index 0000000..0d96d55 --- /dev/null +++ b/src/Inject/Head.php @@ -0,0 +1,33 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/Interceptor.php b/src/Inject/Interceptor.php new file mode 100644 index 0000000..372d9ee --- /dev/null +++ b/src/Inject/Interceptor.php @@ -0,0 +1,14 @@ +middleware); + } + + +} diff --git a/src/Inject/Options.php b/src/Inject/Options.php new file mode 100644 index 0000000..7b016f6 --- /dev/null +++ b/src/Inject/Options.php @@ -0,0 +1,33 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/Other.php b/src/Inject/Other.php new file mode 100644 index 0000000..eaa6497 --- /dev/null +++ b/src/Inject/Other.php @@ -0,0 +1,8 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/Put.php b/src/Inject/Put.php new file mode 100644 index 0000000..86386f9 --- /dev/null +++ b/src/Inject/Put.php @@ -0,0 +1,33 @@ +path, [$class, $method]); + } + +} diff --git a/src/Inject/QueryData.php b/src/Inject/QueryData.php new file mode 100644 index 0000000..7dc5fc0 --- /dev/null +++ b/src/Inject/QueryData.php @@ -0,0 +1,17 @@ + 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 + */ + 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]); + } +} diff --git a/src/Server.php b/src/Server.php new file mode 100644 index 0000000..f0542b7 --- /dev/null +++ b/src/Server.php @@ -0,0 +1,165 @@ +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); + } + + +} diff --git a/src/ServerRequest.php b/src/ServerRequest.php new file mode 100644 index 0000000..4497930 --- /dev/null +++ b/src/ServerRequest.php @@ -0,0 +1,680 @@ +{$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); + } +} diff --git a/src/StreamResponse.php b/src/StreamResponse.php new file mode 100644 index 0000000..9ec027e --- /dev/null +++ b/src/StreamResponse.php @@ -0,0 +1,27 @@ +getBody(); + $total = ceil($this->limit / $body->getSize()); + + for ($i = 0; $i < $total; $i++) { + $body->seek($i); + + $response->write($body->read($this->limit)); + } + $response->end(); + } +} diff --git a/src/Validator/Inject/Email.php b/src/Validator/Inject/Email.php new file mode 100644 index 0000000..8d291a2 --- /dev/null +++ b/src/Validator/Inject/Email.php @@ -0,0 +1,40 @@ +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); + } + } +} diff --git a/src/Validator/Inject/Ignore.php b/src/Validator/Inject/Ignore.php new file mode 100644 index 0000000..1b62e0a --- /dev/null +++ b/src/Validator/Inject/Ignore.php @@ -0,0 +1,19 @@ +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); + } +} diff --git a/src/Validator/Inject/Required.php b/src/Validator/Inject/Required.php new file mode 100644 index 0000000..045457f --- /dev/null +++ b/src/Validator/Inject/Required.php @@ -0,0 +1,17 @@ +value) == $name; + } +} diff --git a/src/Validator/Validator.php b/src/Validator/Validator.php new file mode 100644 index 0000000..3c52baf --- /dev/null +++ b/src/Validator/Validator.php @@ -0,0 +1,65 @@ +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; + } + + +} diff --git a/src/Validator/ValidatorMiddleware.php b/src/Validator/ValidatorMiddleware.php new file mode 100644 index 0000000..78aa15f --- /dev/null +++ b/src/Validator/ValidatorMiddleware.php @@ -0,0 +1,8 @@ +