diff --git a/composer.json b/composer.json index 137d46fb..85b64b6f 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ ], "require": { "php": "^7.1.3", + "ext-libxml": "*", "biurad/biurad-helpers": "^0.1", "fig/http-message-util": "^1.1", "psr/http-factory": "^1.0", diff --git a/src/Concerns/CallableResolver.php b/src/Concerns/CallableResolver.php index 0f66f0b3..d6c4e0d3 100644 --- a/src/Concerns/CallableResolver.php +++ b/src/Concerns/CallableResolver.php @@ -1,4 +1,4 @@ -resolveCallable($toResolve); + if (is_string($resolved) && preg_match(self::CALLABLE_PATTERN, $toResolve, $matches)) { + // check for slim callable as "class:method", "class::method" and "class@method" + $resolved = $this->resolveCallable($matches[1], $matches[3]); } - if (is_string($toResolve) && (!is_callable($toResolve) || false !== strpos($toResolve, '::'))) { - // check for slim callable as "class:method", "class::method" and "class@method" - if (preg_match(self::CALLABLE_PATTERN, $toResolve, $matches)) { - $resolved = $this->resolveCallable($matches[1], $matches[3]); - } + if (is_array($resolved) && !is_callable($resolved) && is_string($resolved[0])) { + $resolved = $this->resolveCallable($resolved[0], $resolved[1]); } - if ( - is_array($resolved) && !$resolved instanceof \Closure && - is_countable($resolved) && is_string($toResolve[0]) - ) { + if (is_array($resolved) && !$resolved instanceof Closure && is_string($toResolve[0])) { $resolved = $this->resolveCallable($resolved[0], $resolved[1]); } @@ -109,7 +101,7 @@ public function resolve($toResolve): callable $resolved = $this->assertCallable($resolved); // Bind new Instance or $this->container to \Closure - if ($resolved instanceof \Closure) { + if ($resolved instanceof Closure) { if (null !== $binded = $this->instance) { $resolved = $resolved->bindTo($binded); } @@ -134,7 +126,7 @@ public function returnType($controllerResponse, ResponseInterface $response): Re if (is_string($controllerResponse) || is_numeric($controllerResponse)) { $response->getBody()->write((string) $controllerResponse); - } elseif (is_array($controllerResponse) || $controllerResponse instanceof \stdClass) { + } elseif (is_array($controllerResponse) || $controllerResponse instanceof stdClass) { $response->getBody()->write(json_encode((array) $controllerResponse)); } @@ -172,10 +164,10 @@ protected function resolveCallable($class, $method = '__invoke'): callable { $instance = $class; - if ($this->container instanceof ContainerInterface && is_string($class)) { + if ($this->container instanceof ContainerInterface && is_string($instance)) { $instance = $this->container->get($class); } else { - if (is_string($class) && !class_exists($class)) { + if (!is_object($class) && !class_exists($class)) { throw new InvalidControllerException(sprintf('Callable %s does not exist', $class)); } @@ -198,12 +190,13 @@ protected function resolveCallable($class, $method = '__invoke'): callable /** * @param Callable $callable * - * @throws \RuntimeException if the callable is not resolvable + * @return Callable + * @throws RuntimeException if the callable is not resolvable */ - protected function assertCallable($callable) + protected function assertCallable($callable): callable { // Maybe could be a class object or RequestHandlerInterface instance - if (!$callable instanceof \Closure && is_object($callable) || is_string($callable)) { + if ((!$callable instanceof Closure && is_object($callable)) || is_string($callable)) { $callable = $this->resolveCallable($callable); } @@ -225,7 +218,7 @@ private function isJson(StreamInterface $stream): bool } $stream->rewind(); - json_decode($stream->getContents()); + json_decode($stream->getContents(), true); return JSON_ERROR_NONE === json_last_error(); } diff --git a/src/Concerns/RouteValidation.php b/src/Concerns/RouteValidation.php index 3b039762..88e4ac89 100644 --- a/src/Concerns/RouteValidation.php +++ b/src/Concerns/RouteValidation.php @@ -26,8 +26,6 @@ use function strlen; use function substr; -use const PHP_VERSION_ID; - trait RouteValidation { /** @@ -41,10 +39,10 @@ trait RouteValidation protected function compareMethod($routeMethod, string $requestMethod): bool { if (is_array($routeMethod)) { - return in_array($requestMethod, $routeMethod); + return in_array($requestMethod, $routeMethod, true); } - return $routeMethod == $requestMethod; + return $routeMethod === $requestMethod; } /** @@ -58,7 +56,7 @@ protected function compareMethod($routeMethod, string $requestMethod): bool */ protected function compareDomain(?string $routeDomain, string $requestDomain, array &$parameters): bool { - return ($routeDomain == null || empty($routeDomain)) || preg_match($routeDomain, $requestDomain, $parameters); + return ($routeDomain === null || empty($routeDomain)) || preg_match($routeDomain, $requestDomain, $parameters); } /** @@ -68,7 +66,7 @@ protected function compareDomain(?string $routeDomain, string $requestDomain, ar * @param string $requestUri * @param array $parameters * - * @return bool + * @return bool|int */ protected function compareUri(string $routeUri, string $requestUri, array &$parameters) { @@ -86,7 +84,6 @@ protected function compareUri(string $routeUri, string $requestUri, array &$para */ protected function compareRedirection(string $routeUri, string $requestUri): ?string { - // Resolve Request Uri. $newRequestUri = '/' === $requestUri ? '/' : rtrim($requestUri, '/'); $newRouteUri = '/' === $routeUri ? $routeUri : rtrim($routeUri, '/'); @@ -98,7 +95,9 @@ protected function compareRedirection(string $routeUri, string $requestUri): ?st if (!empty($paths['route']) && $paths['route'] !== $paths['path']) { return $newRequestUri . $paths['route']; - } elseif (empty($paths['route']) && $paths['route'] !== $paths['path']) { + } + + if (empty($paths['route']) && $paths['route'] !== $paths['path']) { return $newRequestUri; } diff --git a/src/Exceptions/InvalidControllerException.php b/src/Exceptions/InvalidControllerException.php index 66aa53a7..8695a7e2 100644 --- a/src/Exceptions/InvalidControllerException.php +++ b/src/Exceptions/InvalidControllerException.php @@ -19,12 +19,13 @@ namespace Flight\Routing\Exceptions; +use DomainException; use Flight\Routing\Interfaces\ExceptionInterface; /** * Class InvalidControllerException */ -class InvalidControllerException extends \DomainException implements ExceptionInterface +class InvalidControllerException extends DomainException implements ExceptionInterface { // } diff --git a/src/Exceptions/InvalidMiddlewareException.php b/src/Exceptions/InvalidMiddlewareException.php index f916f4cb..0c87a23b 100644 --- a/src/Exceptions/InvalidMiddlewareException.php +++ b/src/Exceptions/InvalidMiddlewareException.php @@ -19,12 +19,28 @@ namespace Flight\Routing\Exceptions; +use DomainException; use Flight\Routing\Interfaces\ExceptionInterface; +use Psr\Http\Server\MiddlewareInterface; +use Psr\Http\Server\RequestHandlerInterface; /** * Class InvalidMiddlewareException */ -class InvalidMiddlewareException extends \DomainException implements ExceptionInterface +class InvalidMiddlewareException extends DomainException implements ExceptionInterface { - // + /** + * @param mixed $middleware The middleware that does not fulfill the + * expectations of MiddlewarePipe::pipe + */ + public static function forMiddleware($middleware) : self + { + return new self(sprintf( + 'Middleware "%s" is neither a string service name, a PHP callable,' + . ' a %s instance, a %s instance, or an array of such arguments', + is_object($middleware) ? get_class($middleware) : gettype($middleware), + MiddlewareInterface::class, + RequestHandlerInterface::class + )); + } } diff --git a/src/Exceptions/MethodNotAllowedException.php b/src/Exceptions/MethodNotAllowedException.php new file mode 100644 index 00000000..a80329e8 --- /dev/null +++ b/src/Exceptions/MethodNotAllowedException.php @@ -0,0 +1,27 @@ + + * @copyright 2019 Biurad Group (https://biurad.com/) + * @license https://opensource.org/licenses/BSD-3-Clause License + * + * @link https://www.biurad.com/projects/routingmanager + * @since Version 0.1 + */ + +namespace Flight\Routing\Exceptions; + +/** + * HTTP 405 exception. + */ +class MethodNotAllowedException extends RouteNotFoundException +{ +} diff --git a/src/Exceptions/RouteNotFoundException.php b/src/Exceptions/RouteNotFoundException.php index 28846bff..1ab1664c 100644 --- a/src/Exceptions/RouteNotFoundException.php +++ b/src/Exceptions/RouteNotFoundException.php @@ -19,12 +19,11 @@ namespace Flight\Routing\Exceptions; -use BiuradPHP\Http\Exceptions\ClientExceptions\NotFoundException; -use Flight\Routing\Interfaces\ExceptionInterface; +use DomainException; /** * Class RouteNotFoundException */ -class RouteNotFoundException extends NotFoundException implements ExceptionInterface +class RouteNotFoundException extends DomainException { } diff --git a/src/Interfaces/CallableResolverInterface.php b/src/Interfaces/CallableResolverInterface.php index 12315d25..3c9eb328 100644 --- a/src/Interfaces/CallableResolverInterface.php +++ b/src/Interfaces/CallableResolverInterface.php @@ -28,6 +28,7 @@ interface CallableResolverInterface * This instance added will be binded to CLosure * * @param object $instance + * @return CallableResolverInterface */ public function addInstanceToClosure(object $instance): CallableResolverInterface; @@ -53,6 +54,8 @@ public function resolve($toResolve): callable; * * @param string|int|array|ResponseInterface $controllerResponse * @param ResponseInterface $response + * + * @return ResponseInterface */ public function returnType($controllerResponse, ResponseInterface $response): ResponseInterface; } diff --git a/src/Interfaces/ResourceController.php b/src/Interfaces/ResourceController.php index 45ba3b24..7d93db5c 100644 --- a/src/Interfaces/ResourceController.php +++ b/src/Interfaces/ResourceController.php @@ -19,6 +19,7 @@ namespace Flight\Routing\Interfaces; +use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface as Request; /** @@ -29,7 +30,7 @@ interface ResourceController /** * Display a listing of the resource. * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function index(); @@ -39,23 +40,23 @@ public function index(); * @param Request $request * @param int|mixed $id * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function show(Request $request, $id); /** * Store a newly created resource in storage. * - * @param \Psr\Http\Message\ServerRequestInterface $request + * @param Request $request * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function store(Request $request); /** * Show the form for creating a new resource. * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function create(); @@ -65,7 +66,7 @@ public function create(); * @param Request $request * @param int|mixed $id * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function edit(Request $request, $id); @@ -75,7 +76,7 @@ public function edit(Request $request, $id); * @param Request $request * @param int|mixed $id * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function update(Request $request, $id); @@ -83,9 +84,9 @@ public function update(Request $request, $id); * Remove the specified resource from storage. * * @param Request $request - * @param id|mixed $id + * @param string|mixed $id * - * @return \Psr\Http\Message\ResponseInterface|string + * @return ResponseInterface|string */ public function destroy(Request $request, $id); diff --git a/src/Interfaces/RouteCollectorInterface.php b/src/Interfaces/RouteCollectorInterface.php index aaf67a80..7a3aeb9b 100644 --- a/src/Interfaces/RouteCollectorInterface.php +++ b/src/Interfaces/RouteCollectorInterface.php @@ -19,11 +19,14 @@ namespace Flight\Routing\Interfaces; +use Closure; +use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Flight\Routing\Exceptions\RouteNotFoundException; use Flight\Routing\Exceptions\UrlGenerationException; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; +use RuntimeException; interface RouteCollectorInterface { @@ -68,8 +71,8 @@ public function getCacheFile(): ?string; * @param string $cacheFile * @return RouteCollectorInterface * - * @throws \InvalidArgumentException - * @throws \RuntimeException + * @throws InvalidArgumentException + * @throws RuntimeException */ public function setCacheFile(string $cacheFile): RouteCollectorInterface; @@ -127,6 +130,9 @@ public function setNamespace(?string $rootNamespace = null): RouteCollectorInter /** * Whether return a permanent redirect. + * @param bool $permanent + * + * @return RouteCollectorInterface */ public function setPermanentRedirection(bool $permanent = true): RouteCollectorInterface; @@ -137,7 +143,7 @@ public function setPermanentRedirection(bool $permanent = true): RouteCollectorI * * @return RouteInterface * - * @throws \RuntimeException If named route does not exist + * @throws RuntimeException If named route does not exist */ public function getNamedRoute(string $name): RouteInterface; @@ -147,18 +153,15 @@ public function getNamedRoute(string $name): RouteInterface; * @param string $name Route name * @return RouteCollectorInterface * - * @throws \RuntimeException If named route does not exist + * @throws RuntimeException If named route does not exist */ public function removeNamedRoute(string $name): RouteCollectorInterface; /** * Lookup a route via the route's unique identifier * - * @param string $identifier - * - * @return RouteInterface - * - * @throws \RuntimeException If route of identifier does not exist + * @param RouteInterface $route + * @return void */ public function addLookupRoute(RouteInterface $route): void; @@ -176,6 +179,13 @@ public function currentRoute(): ?RouteInterface; */ public function getRequest(): ServerRequestInterface; + /** + * Ge the current router used. + * + * @return RouterInterface + */ + public function getRouter(): RouterInterface; + /** * Set the global the middlewares stack attached to all routes. * @@ -200,6 +210,7 @@ public function routeMiddlewares($middlewares = []): RouteCollectorInterface; * This method implements a fluent interface. * * @param array $parameters The parameters + * @param int $type * * @return $this */ @@ -208,8 +219,10 @@ public function addParameters(array $parameters, int $type = self::TYPE_REQUIREM /** * Add route group * - * @param array $attributes + * @param array $attributes * @param string|callable $callable + * + * @return RouteGroupInterface */ public function group(array $attributes = [], $callable): RouteGroupInterface; @@ -219,11 +232,11 @@ public function group(array $attributes = [], $callable): RouteGroupInterface; * Router knows how to respond to resource controller * request automatically * - * @param string $uri + * @param $name * @param Closure|callable|string $controller - * @param array $options + * @param array $options */ - public function resource($name, $controller, array $options = []); + public function resource($name, $controller, array $options = []): void ; /** * Add route diff --git a/src/Interfaces/RouteInterface.php b/src/Interfaces/RouteInterface.php index 7b7ef3e0..9e1dd0ba 100644 --- a/src/Interfaces/RouteInterface.php +++ b/src/Interfaces/RouteInterface.php @@ -35,9 +35,18 @@ public function getMethods(): array; /** * Get the route patternised path. + * + * @return string */ public function getPath(): string; + /** + * Get the Controller used on route. + * + * @return mixed + */ + public function getController(); + /** * Set a regular expression requirement on the route. * @@ -55,6 +64,8 @@ public function getPatterns(): array; /** * Get route domain, same as host. + * + * @return string */ public function getDomain(): string; @@ -63,7 +74,7 @@ public function getDomain(): string; * * @param string|null $domain * - * @return $this|string|null + * @return RouteInterface */ public function setDomain(?string $domain = null): RouteInterface; @@ -114,6 +125,7 @@ public function whereArray(array $wheres = []): RouteInterface; * * @param string $name * @param string $value + * @param bool $includeInSavedArguments * * @return self */ @@ -131,13 +143,10 @@ public function setArguments(array $arguments): RouteInterface; /** * Gets a default value. * - * @param string $name A variable name - * - * @return string|null The default value or defaults when not given - * * @param string $name * @param string|null $default - * @return string|null + * + * @return string|null The default value or defaults when not given */ public function getDefault(string $name, ?string $default = null): ?string; @@ -148,7 +157,7 @@ public function getDefault(string $name, ?string $default = null): ?string; * * @param array $defaults The defaults * - * @return $this + * @return RouteInterface */ public function addDefaults(array $defaults): RouteInterface; @@ -166,14 +175,25 @@ public function getDefaults(): array; * * @return bool true if the default value is set, false otherwise */ - public function hasDefault(string $name); + public function hasDefault(string $name): bool; /** * Checks if route exists in a group + * + * @return bool */ public function hasGroup(): bool; /** + * The group id, route belongs to. + * + * @return string|null + */ + public function getGroupId(): ?string; + + /** + * Add middlewares to route. + * * @param MiddlewareInterface|string|array|callable|RequestHandlerInterface $middleware * @return RouteInterface */ diff --git a/src/Interfaces/RouterInterface.php b/src/Interfaces/RouterInterface.php index 61fc5b37..5911720b 100644 --- a/src/Interfaces/RouterInterface.php +++ b/src/Interfaces/RouterInterface.php @@ -49,8 +49,7 @@ interface RouterInterface * or `generateUri()` have already been called, to ensure integrity of the * router between invocations of either of those methods. * - * @throws \RuntimeException when called after match() or - * generateUri() have been called. + * @param RouteInterface $route */ public function addRoute(RouteInterface $route) : void; @@ -61,6 +60,9 @@ public function addRoute(RouteInterface $route) : void; * request instance, and pass them to the underlying router implementation; * when done, they will then marshal a `RouteResults` instance indicating * the results of the matching operation and return it to the caller. + * + * @param Request $request + * @return RouteResults */ public function match(Request $request) : RouteResults; @@ -74,16 +76,17 @@ public function match(Request $request) : RouteResults; * the URI, this should be performed afterwards; consider passing the URI * to league/uri to encode it. * - * @param RouteInterface $route The Route instance name. + * @param RouteInterface $route The Route instance name. * @param string[]|array $substitutions key => value option pairs to pass to the * router for purposes of generating a URI; takes precedence over options * present in route used to generate URI * + * @return string + * @throws UrlGenerationException if the route name is not known + * or a parameter value does not match its regex * @see https://github.com/auraphp/Aura.Router/blob/3.x/docs/generating-paths.md * @see https://docs.zendframework.com/zend-router/routing/ * - * @throws UrlGenerationException if the route name is not known - * or a parameter value does not match its regex */ public function generateUri(RouteInterface $route, array $substitutions = []) : string; } diff --git a/src/Interfaces/RouterProxyInterface.php b/src/Interfaces/RouterProxyInterface.php index e58cd29c..77c984a6 100644 --- a/src/Interfaces/RouterProxyInterface.php +++ b/src/Interfaces/RouterProxyInterface.php @@ -19,6 +19,8 @@ namespace Flight\Routing\Interfaces; +use Closure; + interface RouterProxyInterface { /** @@ -134,9 +136,9 @@ public function group(array $attributes = [], $callable): RouteGroupInterface; * Router knows how to respond to resource controller * request automatically * - * @param string $uri + * @param $name * @param Closure|callable|string $controller - * @param array $options + * @param array $options * * @return mixed */ diff --git a/src/Middlewares/CacheControlMiddleware.php b/src/Middlewares/CacheControlMiddleware.php index 7d1f8762..a9bbb9f8 100644 --- a/src/Middlewares/CacheControlMiddleware.php +++ b/src/Middlewares/CacheControlMiddleware.php @@ -29,6 +29,8 @@ use function preg_replace; use function stripos; +use const CASE_LOWER; + /** * Set the cache control to no cache * Disable's caching of potentially sensitive data. @@ -56,7 +58,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } // Fix protocol - if ('HTTP/1.0' != $request->getServerParams()['SERVER_PROTOCOL']) { + if ('HTTP/1.0' !== $request->getServerParams()['SERVER_PROTOCOL']) { $response = $response->withProtocolVersion('1.1'); } @@ -65,7 +67,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface // Check if we need to send extra expire info headers if ( - '1.0' == $response->getProtocolVersion() && + '1.0' === $response->getProtocolVersion() && false !== strpos($response->getHeaderLine('Cache-Control'), 'no-cache') ) { $response = $response @@ -81,8 +83,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface } if ( - 301 == $response->getStatusCode() && - !array_key_exists('cache-control', array_change_key_case($response->getHeaders(), \CASE_LOWER)) + 301 === $response->getStatusCode() && + !array_key_exists('cache-control', array_change_key_case($response->getHeaders(), CASE_LOWER)) ) { $response = $response->withoutHeader('Cache-Control'); } @@ -96,10 +98,18 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface * @see http://support.microsoft.com/kb/323308 * * @final + * @param ServerRequestInterface $request + * @param ResponseInterface $response + * + * @return ResponseInterface */ protected function ensureIEOverSSLCompatibility(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { - if (false !== stripos($response->getHeaderLine('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->getServerParams()['HTTP_USER_AGENT'], $match) && true === $request->isSecure()) { + if ( + false !== stripos($response->getHeaderLine('Content-Disposition'), 'attachment') && + 1 === preg_match('/MSIE (.*?);/i', $request->getServerParams()['HTTP_USER_AGENT'], $match) && + array_key_exists('HTTPS', $request->getServerParams()) + ) { if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) { $response = $response->withoutHeader('Cache-Control'); } diff --git a/src/Middlewares/ContentLengthMiddleware.php b/src/Middlewares/ContentLengthMiddleware.php index 4c54a46d..676859bd 100644 --- a/src/Middlewares/ContentLengthMiddleware.php +++ b/src/Middlewares/ContentLengthMiddleware.php @@ -58,12 +58,12 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $response = $response->withoutHeader('Content-Length'); } - if ('HEAD' === $request->getMethod()) { - // cf. RFC2616 14.13 - $length = $response->getHeaderLine('Content-Length'); - if (null !== $length = $response->getBody()->getSize()) { - $response = $response->withHeader('Content-Length', (string) $length); - } + // cf. RFC2616 14.13 + if (('HEAD' === $request->getMethod()) && null !== $response->getBody()->getSize()) { + $response = $response->withHeader( + 'Content-Length', + $response->getHeaderLine('Content-Length') + ); } return $response; diff --git a/src/Middlewares/UriRedirectMiddleware.php b/src/Middlewares/UriRedirectMiddleware.php index 74898e86..b559ac65 100644 --- a/src/Middlewares/UriRedirectMiddleware.php +++ b/src/Middlewares/UriRedirectMiddleware.php @@ -28,12 +28,11 @@ use function in_array; use function is_array; -use function strlen; class UriRedirectMiddleware implements MiddlewareInterface { /** - * @var array|\ArrayAccess + * @var array|ArrayAccess */ protected $redirects = []; @@ -57,6 +56,7 @@ class UriRedirectMiddleware implements MiddlewareInterface /** * @param array|ArrayAccess $redirects [from => to] + * @param array $options */ public function __construct($redirects = [], array $options = self::DEFAUTLS) { @@ -76,6 +76,8 @@ public function __construct($redirects = [], array $options = self::DEFAUTLS) /** * Whether return a permanent redirect. + * @param bool $permanent + * @return UriRedirectMiddleware */ public function permanentRedirection(bool $permanent = true): self { @@ -86,6 +88,8 @@ public function permanentRedirection(bool $permanent = true): self /** * Whether include the query to search the url + * @param bool $query + * @return UriRedirectMiddleware */ public function allowQueries(bool $query = true): self { @@ -96,6 +100,7 @@ public function allowQueries(bool $query = true): self /** * Process a request and return a response. + * @inheritDoc */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { @@ -104,7 +109,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface $uri = $request->getUri()->getPath(); - if ($this->query && strlen($query = $request->getUri()->getQuery()) > 0) { + if ($this->query && $query = $request->getUri()->getQuery() !== '') { $uri .= '?' . $query; } @@ -119,6 +124,8 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface /** * Determine the response code according with the method and the permanent config + * @param ServerRequestInterface $request + * @return int */ private function determineResponseCode(ServerRequestInterface $request): int { diff --git a/src/Route.php b/src/Route.php index 7fb45aeb..a69bdbf5 100644 --- a/src/Route.php +++ b/src/Route.php @@ -1,4 +1,4 @@ -getMessage()); + } catch (Throwable $exception) { + throw new RuntimeException($exception->getMessage()); } } @@ -247,11 +250,13 @@ public function fromArray(array $values): void * * @param string $uri * @param mixed $prefix + * + * @return mixed|string */ private function normalizePrefix(string $uri, $prefix) { // Allow homepage uri on prefix just like python dgango url style. - if (in_array($uri, ['', '/'])) { + if (in_array($uri, ['', '/'], true)) { return rtrim($prefix, '/') . $uri; } @@ -270,7 +275,7 @@ private function normalizePrefix(string $uri, $prefix) * * @param string $pattern The path pattern * - * @return $this + * @return void */ protected function setPath(string $pattern): void { @@ -282,7 +287,9 @@ protected function setPath(string $pattern): void } /** - * @return callable|string + * @param $controller + * + * @return void */ protected function setController($controller): void { @@ -295,9 +302,10 @@ protected function setController($controller): void ) { $controller = $namespace . $controller; } elseif ( - is_array($controller) && !$controller instanceof \Closure && - !is_object($controller[0]) && !class_exists($controller[0]) && count($controller) === 2) - { + is_array($controller) && + !is_callable($controller) && + !class_exists($controller[0]) + ) { $controller[0] = $namespace . $controller[0]; } @@ -305,24 +313,35 @@ protected function setController($controller): void } /** + * @param array $groups * @return void */ protected function appendGroupToRoute(array $groups): void { - if (empty($groups)) return; - $this->groups = $groups[0]->getOptions(); + if (empty($groups)) { + return; + } + $this->groups = $groups[0]->getOptions(); if (isset($this->groups[RouteGroupInterface::MIDDLEWARE])) { - $this->middlewares = array_merge($this->groups[RouteGroupInterface::MIDDLEWARE], $this->middlewares); + $this->middlewares = array_merge($this->middlewares, $this->groups[RouteGroupInterface::MIDDLEWARE]); } if (isset($this->groups[RouteGroupInterface::DOMAIN])) { - $this->setDomain($this->groups[RouteGroupInterface::DOMAIN]); + $this->setDomain($this->groups[RouteGroupInterface::DOMAIN] ?? ''); } $this->groupAppended = true; } + /** + * {@inheritdoc} + */ + public function getController() + { + return $this->controller; + } + /** * {@inheritdoc} */ @@ -331,6 +350,18 @@ public function hasGroup(): bool return $this->groupAppended; } + /** + * {@inheritdoc} + */ + public function getGroupId(): ?string + { + if (!$this->hasGroup()) { + return null; + } + + return md5(serialize($this->groups)); + } + /** * {@inheritdoc} */ @@ -429,8 +460,7 @@ public function setArguments(array $arguments, bool $includeInSavedArguments = t */ public function setName(?string $name): RouteInterface { - $current = isset($this->groups[RouteGroupInterface::NAME]) - ? $this->groups[RouteGroupInterface::NAME] : null; + $current = $this->groups[RouteGroupInterface::NAME] ?? null; $definedName = null !== $current ? $current . $name : $name; if (null !== $current && mb_strpos($current, '.') === false) { @@ -503,9 +533,9 @@ public function getDefaults(): array /** * {@inheritdoc} */ - public function hasDefault($name) + public function hasDefault($name): bool { - return \array_key_exists($name, $this->defaults); + return array_key_exists($name, $this->defaults); } /** @@ -542,7 +572,9 @@ public function prepare(array $arguments): RouteInterface // Add the arguments foreach ($arguments as $k => $v) { - if (is_int($k)) continue; + if (is_int($k)) { + continue; + } $this->setArgument($k, $v); } @@ -552,6 +584,7 @@ public function prepare(array $arguments): RouteInterface /** * {@inheritdoc} + * @throws ReflectionException */ public function run(ServerRequestInterface $request): ResponseInterface { @@ -559,7 +592,10 @@ public function run(ServerRequestInterface $request): ResponseInterface $middlewares = array_merge($this->middlewares, $this->middlewareDispatcher->getMiddlewareStack()); // Allow Middlewares to be disabled - if (in_array('off', $middlewares) || in_array('disable', $middlewares)) { + if ( + in_array('off', $middlewares, true) || + in_array('disable', $middlewares, true) + ) { $middlewares = []; } @@ -576,7 +612,7 @@ public function run(ServerRequestInterface $request): ResponseInterface // encountered from the map thus far. We'll save its current index plus its index // from the priority map so we can compare against them on the next iterations. return $middleware->process($request, $requestHandler); - }; + } } return $this->handle($request); @@ -584,31 +620,41 @@ public function run(ServerRequestInterface $request): ResponseInterface /** * {@inheritdoc} + * @throws ReflectionException */ public function handle(ServerRequestInterface $request): ResponseInterface { $callable = $this->callableResolver->resolve($this->controller); $request = $request->withAttribute('arguments', $this->arguments); - $response = null; // An empty response type. // If controller is instance of RequestHandlerInterface - if ( - is_countable($callable) && count($callable) === 2 && - $callable[0] instanceof RequestHandlerInterface - ) { + if (!$callable instanceof Closure && $callable[0] instanceof RequestHandlerInterface) { return $callable($request); } - if (class_exists(BoundMethod::class)) { - $response = BoundMethod::call($this->container, $callable, $this->arguments + [$request]); - } - return $this->callableResolver->returnType( - $response ?? $callable($request, $this->response, $this->arguments), + $this->handleController($callable, $request), $this->response ); } + /** + * Handles a callable controller served on a route + * + * @param callable $controller + * @param ServerRequestInterface $request + * + * @return mixed + */ + protected function handleController(callable $controller, ServerRequestInterface $request) + { + if (class_exists(BoundMethod::class)) { + return BoundMethod::call($this->container, $controller, $this->arguments + [$request]); + } + + return $controller($request, $this->response, $this->arguments); + } + /** * @param array $data * @param string $key @@ -627,7 +673,7 @@ private function getValueFromKey(array $data, string $key, string $message = nul $message = sprintf('Missing "%s" key in route collection', $key); } - throw new \RuntimeException($message); + throw new RuntimeException($message); } /** diff --git a/src/RouteCollector.php b/src/RouteCollector.php index 1db91572..005a2bb1 100644 --- a/src/RouteCollector.php +++ b/src/RouteCollector.php @@ -19,9 +19,11 @@ namespace Flight\Routing; +use Closure; +use DomainException; use Flight\Routing\Concerns\HttpMethods; +use Flight\Routing\Exceptions\MethodNotAllowedException; use Psr\Http\Message\ServerRequestInterface; -use BiuradPHP\Http\Exceptions\ClientExceptions; use Flight\Routing\RouteResource as Resource; use Flight\Routing\Exceptions\RouteNotFoundException; use Flight\Routing\Exceptions\UrlGenerationException; @@ -35,13 +37,13 @@ use Psr\Http\Message\ResponseFactoryInterface; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; +use RuntimeException; use function file_exists; use function is_readable; use function is_writable; use function dirname; use function sprintf; -use function array_push; use function http_build_query; use function str_replace; use function in_array; @@ -128,9 +130,9 @@ class RouteCollector implements Interfaces\RouteCollectorInterface, LoggerAwareI /** * Base path used in pathFor() * - * @var string + * @var string|null */ - protected $basePath = null; + protected $basePath; /** * Path to fast route cache file. Set to false to disable route caching @@ -213,13 +215,13 @@ public function getCacheFile(): ?string public function setCacheFile(string $cacheFile): RouteCollectorInterface { if (file_exists($cacheFile) && !is_readable($cacheFile)) { - throw new \RuntimeException( + throw new RuntimeException( sprintf('Route collector cache file `%s` is not readable', $cacheFile) ); } if (!file_exists($cacheFile) && !is_writable(dirname($cacheFile))) { - throw new \RuntimeException( + throw new RuntimeException( sprintf('Route collector cache file directory `%s` is not writable', dirname($cacheFile)) ); } @@ -260,7 +262,7 @@ public function setBasePath(string $basePath): RouteCollectorInterface public function addParameters(array $parameters, int $type = self::TYPE_REQUIREMENT): RouteCollectorInterface { foreach ($parameters as $key => $regex) { - if (self::TYPE_DEFAULT == $type) { + if (self::TYPE_DEFAULT === $type) { $this->setGroupOption(RouteGroupInterface::DEFAULTS, [$key => $regex]); break; } @@ -288,9 +290,9 @@ public function setPermanentRedirection(bool $permanent = true): RouteCollectorI * @param string $uri * @param mixed $action * - * @return \Flight\Routing\Route + * @return Route */ - protected function createRoute($methods, $uri, $action) + protected function createRoute($methods, $uri, $action): Route { $route = $this->newRoute($methods, $uri, $action); @@ -313,9 +315,9 @@ protected function createRoute($methods, $uri, $action) * @param string $uri * @param mixed $action * - * @return \Flight\Routing\Route + * @return Route */ - protected function newRoute($methods, $uri, $action) + protected function newRoute($methods, $uri, $action): Route { $route = new Route( (array) $methods, @@ -339,13 +341,13 @@ protected function newRoute($methods, $uri, $action) * * @param array|array $methods HTTP method to accept * @param string|null $uri the uri of the route - * @param \Closure|array|string|null $action a requesthandler or controller + * @param Closure|array|string|null $action a requesthandler or controller * - * @return \Flight\Routing\Route + * @return Route * - * @throws \RuntimeException when called after match() have been called. + * @throws RuntimeException when called after match() have been called. */ - protected function addRoute($methods, $uri, $action) + protected function addRoute($methods, $uri, $action): Route { $route = $this->createRoute($methods, $uri, $action); $domainAndUri = $route->getDomain() . $route->getPath(); @@ -356,7 +358,7 @@ protected function addRoute($methods, $uri, $action) } // Resolve Routing - array_push($this->routes, $route); + $this->routes[] = $route; $this->router->addRoute($route); return $route; @@ -400,7 +402,7 @@ public function getNamedRoute(string $name): RouteInterface return $route; } } - throw new \RuntimeException('Named route does not exist for name: ' . $name); + throw new RuntimeException('Named route does not exist for name: ' . $name); } /** @@ -409,7 +411,7 @@ public function getNamedRoute(string $name): RouteInterface public function addLookupRoute(RouteInterface $route): void { if (null === $name = $route->getName()) { - //throw new \RuntimeException('Route not found, looks like your route cache is stale.'); + //throw new RuntimeException('Route not found, looks like your route cache is stale.'); return; } @@ -425,7 +427,7 @@ public function group(array $attributes = [], $callable): RouteGroupInterface $oldGroupOption = $this->groupOptions; $routeCollectorProxy = new RouterProxy($this->request, $this->responseFactory, $this->router, $this); - $prefixPattern = isset($attributes[RouteGroupInterface::PREFIX]) ? $attributes[RouteGroupInterface::PREFIX] : null; + $prefixPattern = $attributes[RouteGroupInterface::PREFIX] ?? null; // Register the Route Grouping $routeGroup = new RouteGroup($prefixPattern, $attributes, $callable, $this->callableResolver, $routeCollectorProxy); @@ -448,11 +450,11 @@ public function group(array $attributes = [], $callable): RouteGroupInterface * Register a new GET route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function get($uri, $action = null) + public function get($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_GET, HttpMethods::METHOD_HEAD], $uri, $action); } @@ -461,11 +463,11 @@ public function get($uri, $action = null) * Register a new POST route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function post($uri, $action = null) + public function post($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_POST], $uri, $action); } @@ -474,11 +476,11 @@ public function post($uri, $action = null) * Register a new PUT route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function put($uri, $action = null) + public function put($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_PUT], $uri, $action); } @@ -487,11 +489,11 @@ public function put($uri, $action = null) * Register a new PATCH route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function patch($uri, $action = null) + public function patch($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_PATCH], $uri, $action); } @@ -500,11 +502,11 @@ public function patch($uri, $action = null) * Register a new DELETE route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function delete($uri, $action = null) + public function delete($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_DELETE], $uri, $action); } @@ -513,11 +515,11 @@ public function delete($uri, $action = null) * Register a new OPTIONS route with the router. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function options($uri, $action = null) + public function options($uri, $action = null): RouteInterface { return $this->map([HttpMethods::METHOD_OPTIONS], $uri, $action); } @@ -526,11 +528,11 @@ public function options($uri, $action = null) * Register a new route responding to all verbs. * * @param string $uri - * @param \Closure|array|string|null $action + * @param Closure|array|string|null $action * - * @return \Flight\Routing\Route + * @return RouteInterface */ - public function any($uri, $action = null) + public function any($uri, $action = null): RouteInterface { return $this->map([HttpMethods::HTTP_METHODS_STANDARD], $uri, $action); } @@ -551,7 +553,7 @@ public function map(array $methods, $uri, $action = null): RouteInterface */ public function resourceParameters(array $parameters = []) { - return Resource::setParameters($parameters); + Resource::setParameters($parameters); } /** @@ -561,7 +563,7 @@ public function resourceParameters(array $parameters = []) * * @return array|null */ - public function resourceVerbs(array $verbs = []) + public function resourceVerbs(array $verbs = []): ?array { return Resource::verbs($verbs); } @@ -581,11 +583,11 @@ public function resources(array $resources) /** * {@inheritdoc} */ - public function resource($name, $controller, array $options = []) + public function resource($name, $controller, array $options = []): void { $registrar = new Resource($this); - return $registrar->register($name, $controller, $options); + $registrar->register($name, $controller, $options); } /** @@ -625,14 +627,14 @@ public function setNamespace(?string $rootNamespace = null): RouteCollectorInter */ public function generateUri(string $routeName, array $parameters = [], array $queryParams = []): ?string { - if (isset($this->nameList[$routeName]) == false) { + if (isset($this->nameList[$routeName]) === false) { throw new UrlGenerationException(sprintf('Unable to generate a URL for the named route "%s" as such route does not exist.', $routeName), 404); } // First we will construct the entire URI including the root. Once it // has been constructed, we'll pass the remaining for further parsing. $uri = $this->router->generateUri($route = $this->getNamedRoute($routeName), $parameters); - $uri = str_replace('/?', '', rtrim($uri, "/")); + $uri = str_replace('/?', '', rtrim($uri, '/')); // Resolve port and domain for better url generated from route by name. $domain = '.'; @@ -655,7 +657,7 @@ public function generateUri(string $routeName, array $parameters = [], array $qu // Incase query is added to uri. if (!empty($queryParams)) { - $url .= '?' . http_build_query($queryParams); + $uri .= '?' . http_build_query($queryParams); } return $domain . $uri; @@ -669,6 +671,14 @@ public function currentRoute(): ?RouteInterface return $this->currentRoute; } + /** + * {@inheritdoc} + */ + public function getRouter(): RouterInterface + { + return $this->router; + } + /** * {@inheritdoc} */ @@ -700,21 +710,22 @@ public function dispatch(): ResponseInterface return $response; case RouteResults::NOT_FOUND: - $exception = new RouteNotFoundException(); - $exception->withMessage(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $uriPath)); - - throw $exception; + throw new RouteNotFoundException(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $uriPath)); case RouteResults::METHOD_NOT_ALLOWED: - throw new ClientExceptions\MethodNotAllowedException(); + throw new MethodNotAllowedException(sprintf( + 'Unable to find route on requested "%s" method', $this->request->getMethod() + )); default: - throw new \DomainException('An unexpected error occurred while performing routing.'); + throw new DomainException('An unexpected error occurred while performing routing.'); } } /** * Determine the response code according with the method and the permanent config + * @param ServerRequestInterface $request + * @return int */ public function determineResponseCode(ServerRequestInterface $request): int { @@ -733,7 +744,7 @@ public function determineResponseCode(ServerRequestInterface $request): int */ protected function getGroupOption(string $name) { - return isset($this->groupOptions[$name]) ? $this->groupOptions[$name] : null; + return $this->groupOptions[$name] ?? null; } /** @@ -751,8 +762,9 @@ protected function setGroupOption(string $name, $value): void * Resolving patterns and defaults to group * * @param array $groupOptions + * @return array */ - private function resolveGlobals(array $groupOptions): array + protected function resolveGlobals(array $groupOptions): array { $groupOptions[RouteGroup::REQUIREMENTS] = array_merge( $this->getGroupOption(RouteGroup::REQUIREMENTS) ?? [], diff --git a/src/RouteGroup.php b/src/RouteGroup.php index 6b308452..f04872a4 100644 --- a/src/RouteGroup.php +++ b/src/RouteGroup.php @@ -19,8 +19,8 @@ namespace Flight\Routing; +use Closure; use Flight\Routing\Interfaces\CallableResolverInterface; -use Flight\Routing\Interfaces\RouteCollectorInterface; use Flight\Routing\Interfaces\RouteGroupInterface; use Flight\Routing\Interfaces\RouterProxyInterface; @@ -54,11 +54,11 @@ class RouteGroup implements RouteGroupInterface protected $attributes = []; /** - * @param string|null $pattern - * @param array $attributes - * @param callable|string $callable - * @param CallableResolverInterface $callableResolver - * @param RouteCollectorInterface $routeProxy + * @param string|null $pattern + * @param array $attributes + * @param callable|string $callable + * @param CallableResolverInterface $callableResolver + * @param RouterProxyInterface $routeProxy */ public function __construct(?string $pattern, array $attributes, $callable, CallableResolverInterface $callableResolver, RouterProxyInterface $routeProxy) { @@ -94,7 +94,6 @@ public function addOptions(array $attributes): void /** * Get Route The Group Option. * - * @param string $name * @return mixed */ public function getOptions(): array @@ -113,7 +112,7 @@ public function getPattern(): ?string /** * Load the provided routes from group. * - * @param \Closure|callable|string $routes + * @param Closure|callable|string $routes * * @return mixed */ diff --git a/src/RouteMiddleware.php b/src/RouteMiddleware.php index 4aea9ad3..4a455114 100644 --- a/src/RouteMiddleware.php +++ b/src/RouteMiddleware.php @@ -23,7 +23,7 @@ use Psr\Http\Server\MiddlewareInterface; use Psr\Container\ContainerInterface; use Psr\Http\Server\RequestHandlerInterface; -use BiuradPHP\Http\Exceptions\InvalidMiddlewareException; +use Flight\Routing\Exceptions\InvalidMiddlewareException; use Laminas\Stratigility\Middleware\RequestHandlerMiddleware; use Laminas\Stratigility\Middleware\CallableMiddlewareDecorator; @@ -88,7 +88,7 @@ class RouteMiddleware protected $container; /** - * @param RequestHandlerInterface $kernel + * @param array $routeMiddlewares * @param ContainerInterface|null $container */ public function __construct(array $routeMiddlewares, ContainerInterface $container = null) { @@ -146,6 +146,7 @@ public function getMiddlewareStack(): array * Resolve a middleware so it can be used flexibly. * * @param MiddlewareInterface|string|callable $middleware + * @return MiddlewareInterface */ public function resolve($middleware): MiddlewareInterface { diff --git a/src/RouteResource.php b/src/RouteResource.php index 39bab874..4b7f4b49 100644 --- a/src/RouteResource.php +++ b/src/RouteResource.php @@ -1,4 +1,4 @@ -router = $router; } @@ -97,7 +95,7 @@ public function __construct(RouteCollectorInterface $router) * @param string $controller * @param array $options */ - public function register($name, $controller, array $options = []) + public function register($name, $controller, array $options = []): void { if (isset($options['parameters']) && !isset($this->parameters)) { $this->parameters = $options['parameters']; @@ -122,11 +120,13 @@ public function register($name, $controller, array $options = []) * * @return array */ - protected function getResourceMethods($defaults, $options) + protected function getResourceMethods($defaults, $options): array { if (isset($options['only'])) { return array_intersect($defaults, (array) $options['only']); - } elseif (isset($options['except'])) { + } + + if (isset($options['except'])) { return array_diff($defaults, (array) $options['except']); } @@ -140,7 +140,7 @@ protected function getResourceMethods($defaults, $options) * * @return string */ - public function getResourceUri($resource) + public function getResourceUri($resource): string { if (!mb_strpos($resource, '.')) { return $resource; @@ -163,7 +163,7 @@ public function getResourceUri($resource) * * @return string */ - protected function getNestedResourceUri(array $segments) + protected function getNestedResourceUri(array $segments): string { // We will spin through the segments and create a place-holder for each of the // resource segments, as well as the resource itself. Then we should get an @@ -183,7 +183,7 @@ protected function getNestedResourceUri(array $segments) * * @return array */ - protected function getResourceAction($resource, $controller, $method, $options) + protected function getResourceAction($resource, $controller, $method, $options): array { $name = $this->getResourceName($resource, $method, $options); @@ -199,7 +199,7 @@ protected function getResourceAction($resource, $controller, $method, $options) * * @return string */ - protected function getResourceName($resource, $method, $options) + protected function getResourceName($resource, $method, $options): string { // If a global prefix has been assigned to all names for this resource, we will // grab that so we can prepend it onto the name when we create this name for @@ -218,7 +218,7 @@ protected function getResourceName($resource, $method, $options) * * @return string */ - protected function getGroupResourceName($prefix, $resource, $method) + protected function getGroupResourceName($prefix, $resource, $method): string { return trim("{$prefix}{$resource}.{$method}", '.'); } @@ -230,7 +230,7 @@ protected function getGroupResourceName($prefix, $resource, $method) * * @return string */ - public function getResourceWildcard($value) + public function getResourceWildcard($value): string { if (isset($this->parameters[$value])) { $value = $this->parameters[$value]; @@ -249,9 +249,9 @@ public function getResourceWildcard($value) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceIndex($name, $base, $controller, $options) + protected function addResourceIndex($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name); $action = $this->getResourceAction($name, $controller, 'index', $options); @@ -267,9 +267,9 @@ protected function addResourceIndex($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceCreate($name, $base, $controller, $options) + protected function addResourceCreate($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name).'/'.static::$verbs['create']; $action = $this->getResourceAction($name, $controller, 'create', $options); @@ -285,9 +285,9 @@ protected function addResourceCreate($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceStore($name, $base, $controller, $options) + protected function addResourceStore($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name); $action = $this->getResourceAction($name, $controller, 'store', $options); @@ -303,9 +303,9 @@ protected function addResourceStore($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceShow($name, $base, $controller, $options) + protected function addResourceShow($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name).'/{'.$base.'}'; $action = $this->getResourceAction($name, $controller, 'show', $options); @@ -321,9 +321,9 @@ protected function addResourceShow($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceEdit($name, $base, $controller, $options) + protected function addResourceEdit($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name).'/{'.$base.'}/'.static::$verbs['edit']; $action = $this->getResourceAction($name, $controller, 'edit', $options); @@ -339,9 +339,9 @@ protected function addResourceEdit($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceUpdate($name, $base, $controller, $options) + protected function addResourceUpdate($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name).'/{'.$base.'}'; $action = $this->getResourceAction($name, $controller, 'update', $options); @@ -357,14 +357,14 @@ protected function addResourceUpdate($name, $base, $controller, $options) * @param string $controller * @param array $options * - * @return \Flight\Routing\RouteCollector + * @return Interfaces\RouteInterface */ - protected function addResourceDestroy($name, $base, $controller, $options) + protected function addResourceDestroy($name, $base, $controller, $options): Interfaces\RouteInterface { $uri = $this->getResourceUri($name).'/{'.$base.'}'; if (static::$verbs['destroy']) { - $uri = $uri.'/'.static::$verbs['destroy']; + $uri .= '/' . static::$verbs['destroy']; } $action = $this->getResourceAction($name, $controller, 'destroy', $options); @@ -377,7 +377,7 @@ protected function addResourceDestroy($name, $base, $controller, $options) * * @return array */ - public static function getParameters() + public static function getParameters(): array { return static::$parameterMap; } @@ -387,7 +387,7 @@ public static function getParameters() * * @param array $parameters */ - public static function setParameters(array $parameters = []) + public static function setParameters(array $parameters = []): void { static::$parameterMap = $parameters; } @@ -399,12 +399,12 @@ public static function setParameters(array $parameters = []) * * @return array */ - public static function verbs(array $verbs = []) + public static function verbs(array $verbs = []): ?array { if (empty($verbs)) { return static::$verbs; - } else { - static::$verbs = array_merge(static::$verbs, $verbs); } + + static::$verbs = array_merge(static::$verbs, $verbs); } } diff --git a/src/RouteResults.php b/src/RouteResults.php index 513ce53e..5896b84a 100644 --- a/src/RouteResults.php +++ b/src/RouteResults.php @@ -93,11 +93,11 @@ class RouteResults implements RequestHandlerInterface, LoggerAwareInterface protected $routeArguments; /** - * @param string $method - * @param string $uri - * @param int $routeStatus - * @param string|null $routeIdentifier - * @param array $routeArguments + * @param string $method + * @param string $uri + * @param int $routeStatus + * @param array $routeArguments + * @param RouteInterface|null $routeIdentifier */ public function __construct( string $method, @@ -114,6 +114,7 @@ public function __construct( } /** + * @param bool $urlDecode * @return RouteInterface|null */ public function getRouteIdentifier(bool $urlDecode = true): ?RouteInterface @@ -149,7 +150,7 @@ public function getRouteStatus(): int } /** - * @return bool + * @return string|null */ public function getRedirectLink(): ?string { @@ -157,6 +158,7 @@ public function getRedirectLink(): ?string } /** + * @param string $uriPath * @return $this|self */ public function shouldRedirect(string $uriPath): RouteResults @@ -187,6 +189,8 @@ public function getAllowedMethods() : ?array * * Otherwise, it processes the composed handle using the provide request * and handler. + * @param ServerRequestInterface $request + * @return ResponseInterface */ public function handle(ServerRequestInterface $request) : ResponseInterface { diff --git a/src/Services/DefaultFlightRouter.php b/src/Services/DefaultFlightRouter.php index c527582c..d20dd0e7 100644 --- a/src/Services/DefaultFlightRouter.php +++ b/src/Services/DefaultFlightRouter.php @@ -77,6 +77,7 @@ public function __construct(SymfonyRouteCompiler $compiler = null) * * Uses Symfony routing style. Since it has been adopted * by many projects and framework including laravel framework. + * @param RouteInterface $route */ public function addRoute(RouteInterface $route): void { @@ -88,7 +89,6 @@ public function addRoute(RouteInterface $route): void */ public function match(ServerRequestInterface $request): RouteResults { - $method = $request->getMethod(); $domain = $request->getUri()->getHost(); // HEAD and GET are equivalent as per RFC @@ -176,7 +176,9 @@ public function generateUri(RouteInterface $route, array $substitutions = []): s $uri = preg_replace_callback('/\??\{(.*?)\??\}/', function ($match) use ($substitutions, $defaults) { if (isset($substitutions[$match[1]])) { return $substitutions[$match[1]]; - } elseif (array_key_exists($match[1], $defaults)) { + } + + if (array_key_exists($match[1], $defaults)) { return $defaults[$match[1]]; } diff --git a/src/Services/HttpPublisher.php b/src/Services/HttpPublisher.php index 6b912630..e7f60779 100644 --- a/src/Services/HttpPublisher.php +++ b/src/Services/HttpPublisher.php @@ -33,7 +33,7 @@ class HttpPublisher implements PublisherInterface /** * {@inheritdoc} * - * @throws \LogicException + * @throws LogicException */ public function publish($content, ?EmitterInterface $emitter): bool { @@ -57,6 +57,8 @@ public function publish($content, ?EmitterInterface $emitter): bool /** * Emit the message body. + * @param StreamInterface $body + * @return bool */ private function emitStreamBody(StreamInterface $body) : bool { @@ -78,6 +80,7 @@ private function emitStreamBody(StreamInterface $body) : bool /** * Emit the response header. + * @param PsrResponseInterface $response */ private function emitResponseHeaders(PsrResponseInterface $response) : void { @@ -85,7 +88,7 @@ private function emitResponseHeaders(PsrResponseInterface $response) : void foreach ($response->getHeaders() as $name => $values) { $name = ucwords($name, '-'); // Filter a header name to wordcase - $first = $name === 'Set-Cookie' ? false : true; + $first = $name !== 'Set-Cookie'; foreach ($values as $value) { header(sprintf('%s: %s', $name, $value), $first, $statusCode); diff --git a/src/Services/SymfonyRouteCompiler.php b/src/Services/SymfonyRouteCompiler.php index 221fbcba..46423f01 100644 --- a/src/Services/SymfonyRouteCompiler.php +++ b/src/Services/SymfonyRouteCompiler.php @@ -56,15 +56,15 @@ */ class SymfonyRouteCompiler { - const REGEX_DELIMITER = '#'; - const DEFAULT_DISPATCH_REGEX = '[^/]++'; + public const REGEX_DELIMITER = '#'; + public const DEFAULT_DISPATCH_REGEX = '[^/]++'; /** * This string defines the characters that are automatically considered separators in front of * optional placeholders (with default and no static text following). Such a single separator * can be left out together with the optional placeholder from matching and generating URLs. */ - const SEPARATORS = '/,;.:-_~+*=@|'; + public const SEPARATORS = '/,;.:-_~+*=@|'; /** * The maximum supported length of a PCRE subpattern name @@ -72,7 +72,7 @@ class SymfonyRouteCompiler * * @internal */ - const VARIABLE_MAXIMUM_LENGTH = 32; + public const VARIABLE_MAXIMUM_LENGTH = 32; private $variables; private $tokens; @@ -88,6 +88,9 @@ class SymfonyRouteCompiler * Sets the pattern for the path. * * Based on https://github.com/symfony/routing/blob/master/Route.php by Fabien + * @param RouteInterface $route + * @param bool $isHost + * @return array */ protected function fixRouteUri(RouteInterface $route, bool $isHost = false): array { @@ -128,9 +131,10 @@ protected function fixRouteUri(RouteInterface $route, bool $isHost = false): arr /** * Get the route requirements. * + * @param array $requirements * @return array */ - protected function getRouteRequirements(array $requirements) + protected function getRouteRequirements(array $requirements): array { $newParamters = []; foreach ($requirements as $key => $regex) { @@ -143,12 +147,8 @@ protected function getRouteRequirements(array $requirements) /** * Match the RouteInterface instance and compiles the current route instance. * - * @throws InvalidArgumentException if a path variable is named _fragment - * @throws LogicException if a variable is referenced more than once - * @throws DomainException if a variable name starts with a digit or if it is too long to be successfully used as - * a PCRE subpattern - * @throws LogicException If the Route cannot be compiled because the - * path or host pattern is invalid + * @param RouteInterface $route + * @return SymfonyRouteCompiler */ public function compile(RouteInterface $route): self { @@ -174,7 +174,7 @@ public function compile(RouteInterface $route): self foreach ($pathVariables as $pathParam) { if ('_fragment' === $pathParam) { - throw new \InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath())); + throw new InvalidArgumentException(sprintf('Route pattern "%s" cannot contain "_fragment" as a path parameter.', $route->getPath())); } } @@ -195,7 +195,7 @@ public function compile(RouteInterface $route): self * * @return string The static prefix */ - public function getStaticPrefix() + public function getStaticPrefix(): string { return $this->staticPrefix; } @@ -205,7 +205,7 @@ public function getStaticPrefix() * * @return string The static regex */ - public function getStaticRegex() + public function getStaticRegex(): string { return $this->staticRegex; } @@ -215,7 +215,7 @@ public function getStaticRegex() * * @return string The regex */ - public function getRegex() + public function getRegex(): string { return $this->regex; } @@ -225,7 +225,7 @@ public function getRegex() * * @return string|null The host regex or null */ - public function getHostRegex() + public function getHostRegex(): ?string { return $this->hostRegex; } @@ -235,7 +235,7 @@ public function getHostRegex() * * @return array The tokens */ - public function getTokens() + public function getTokens(): array { return $this->tokens; } @@ -245,7 +245,7 @@ public function getTokens() * * @return array The tokens */ - public function getHostTokens() + public function getHostTokens(): array { return $this->hostTokens; } @@ -255,7 +255,7 @@ public function getHostTokens() * * @return array The variables */ - public function getVariables() + public function getVariables(): array { return array_unique($this->variables); } @@ -265,7 +265,7 @@ public function getVariables() * * @return array The variables */ - public function getPathVariables() + public function getPathVariables(): array { return $this->pathVariables; } @@ -275,14 +275,14 @@ public function getPathVariables() * * @return array The variables */ - public function getHostVariables() + public function getHostVariables(): array { return $this->hostVariables; } private function sanitizeRequirement(string $key, string $regex) { - if ('' !== $regex && '^' === $regex[0]) { + if ('' !== $regex && strpos($regex, '^') === 0) { $regex = (string) mb_substr($regex, 1); // returns false for a single character } @@ -324,7 +324,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is $precedingText = substr($pattern, $pos, $match[0][1] - $pos); $pos = $match[0][1] + strlen($match[0][0]); - if (!strlen($precedingText)) { + if ($precedingText === '') { $precedingChar = ''; } elseif ($useUtf8) { preg_match('/.$/u', $precedingText, $precedingChar); @@ -339,7 +339,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is if (preg_match('/^\d/', $varName)) { throw new DomainException(sprintf('Variable name "%s" cannot start with a digit in route pattern "%s". Please use a different name.', $varName, $pattern)); } - if (in_array($varName, $variables)) { + if (in_array($varName, $variables, true)) { throw new LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $pattern, $varName)); } @@ -349,7 +349,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is if ($isSeparator && $precedingText !== $precedingChar) { $tokens[] = ['text', substr($precedingText, 0, -strlen($precedingChar))]; - } elseif (!$isSeparator && strlen($precedingText) > 0) { + } elseif (!$isSeparator && $precedingText !== '') { $tokens[] = ['text', $precedingText]; } @@ -386,7 +386,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is if (!$useUtf8 && $needsUtf8) { throw new LogicException(sprintf('Cannot mix UTF-8 requirement with non-UTF-8 charset for variable "%s" in pattern "%s".', $varName, $pattern)); } - $regexp = self::transformCapturingGroupsToNonCapturings($regexp); + $regexp = $this->transformCapturingGroupsToNonCapturings($regexp); } if ($important) { @@ -420,7 +420,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is // compute the matching regexp $regexp = ''; for ($i = 0, $nbToken = count($tokens); $i < $nbToken; ++$i) { - $regexp .= self::computeRegexp($tokens, $i, $firstOptional); + $regexp .= $this->computeRegexp($tokens, $i, $firstOptional); } $regexp = self::REGEX_DELIMITER.'^'.$regexp.'$'.self::REGEX_DELIMITER.'sD'.($isHost ? 'i' : ''); @@ -435,7 +435,7 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is } return [ - 'staticPrefix' => self::determineStaticPrefix($route, $tokens), + 'staticPrefix' => $this->determineStaticPrefix($route, $tokens), 'regex' => $regexp, 'tokens' => array_reverse($tokens), 'variables' => $variables, @@ -444,6 +444,9 @@ private function compilePattern(RouteInterface $route, string $pattern, bool $is /** * Determines the longest static prefix possible for a route. + * @param RouteInterface $route + * @param array $tokens + * @return string */ private function determineStaticPrefix(RouteInterface $route, array $tokens): string { @@ -457,15 +460,18 @@ private function determineStaticPrefix(RouteInterface $route, array $tokens): st $prefix .= $tokens[1][1]; } - return rtrim($prefix, "/"); + return rtrim($prefix, '/'); } /** * Returns the next static character in the Route pattern that will serve as a separator (or the empty string when none available). + * @param string $pattern + * @param bool $useUtf8 + * @return string */ private function findNextSeparator(string $pattern, bool $useUtf8): string { - if ('' == $pattern) { + if ('' === $pattern) { // return empty string if pattern is empty or false (false which can be returned by substr) return ''; } @@ -495,33 +501,32 @@ private function computeRegexp(array $tokens, int $index, int $firstOptional): s if ('text' === $token[0]) { // Text tokens return preg_quote($token[1], self::REGEX_DELIMITER); - } else { - // Variable tokens - if (0 === $index && 0 === $firstOptional) { - // When the only token is an optional variable token, the separator is required - return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); - } else { - $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); - if ($index >= $firstOptional) { - // Enclose each optional token in a subpattern to make it optional. - // "?:" means it is non-capturing, i.e. the portion of the subject string that - // matched the optional subpattern is not passed back. - $regexp = "(?:$regexp"; - $nbTokens = count($tokens); - if ($nbTokens - 1 == $index) { - // Close the optional subpatterns - $regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); - } - } + } - return $regexp; + if (0 === $index && 0 === $firstOptional) { + // When the only token is an optional variable token, the separator is required + return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + } + + $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], self::REGEX_DELIMITER), $token[3], $token[2]); + if ($index >= $firstOptional) { + // Enclose each optional token in a subpattern to make it optional. + // "?:" means it is non-capturing, i.e. the portion of the subject string that + // matched the optional subpattern is not passed back. + $regexp = "(?:$regexp"; + $nbTokens = count($tokens); + if ($nbTokens - 1 === $index) { + // Close the optional subpatterns + $regexp .= str_repeat(')?', $nbTokens - $firstOptional - (0 === $firstOptional ? 1 : 0)); } } + + return $regexp; } private function transformCapturingGroupsToNonCapturings(string $regexp): string { - for ($i = 0; $i < strlen($regexp); ++$i) { + for ($i = 0, $iMax = strlen($regexp); $i < $iMax; ++$i) { if ('\\' === $regexp[$i]) { ++$i; continue;