diff --git a/.circleci/config.yml b/.circleci/config.yml index dac96a0..1818ac6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -22,6 +22,7 @@ jobs: - ./vendor key: v1-dependencies-{{ checksum "composer.json" }} + - run: composer lint - run: composer phpcs - run: composer phpstan - run: composer test diff --git a/.gitignore b/.gitignore index 987e2a2..e96516b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ composer.lock vendor +.phpunit.result.cache diff --git a/composer.json b/composer.json index 153252a..e3a4124 100644 --- a/composer.json +++ b/composer.json @@ -13,24 +13,29 @@ }, "require": { "php": ">=7.2", - "nette/neon": "^2.4", - "slim/slim": "^3.9", - "nette/utils": "^2.5", - "nette/di": "^2.4", + "nette/neon": "^3.0", + "slim/slim": "^3.12", + "nette/utils": "^3.0", + "nette/di": "^3.0", "psr/http-message": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^7", + "phpunit/phpunit": "^8", "roave/security-advisories": "dev-master", - "phpstan/phpstan": "^0.10", - "mockery/mockery": "dev-master", - "slevomat/coding-standard": "^4.8", - "phpstan/phpstan-phpunit": "^0.10.0" + "mockery/mockery": "^1.2", + "brandembassy/coding-standard": "^5.3" }, "scripts": { - "test": "vendor/bin/phpunit tests", - "phpcs": "vendor/bin/phpcs --standard=ruleset.xml src tests --ignore=tests/temp", - "phpcbf": "./vendor/bin/phpcbf --standard=ruleset.xml src tests --ignore=tests/temp", - "phpstan": "vendor/bin/phpstan analyse src tests -l max -c phpstan.neon" + "lint": " find src tests -name \"*.php\" -print0 | xargs -0 -n1 -P8 php -l", + "phpcs": "./vendor/bin/phpcs --ignore=\"tests/temp/*\" --standard=ruleset.xml src tests", + "phpcbf": "./vendor/bin/phpcbf --standard=ruleset.xml src tests", + "phpstan": "./vendor/bin/phpstan analyse src tests", + "test": "./vendor/bin/phpunit tests" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true, + "process-timeout": 600 } } diff --git a/phpstan.neon b/phpstan.neon index 9128f66..69ecde1 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,8 +2,6 @@ parameters: excludes_analyse: - %rootDir%/../../../tests/temp/* - ignoreErrors: - - '#Call to an undefined method Mockery\\ExpectationInterface|Mockery\\HigherOrderMessage::andReturn().#' - includes: - - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/brandembassy/coding-standard/default-phpstan.neon + diff --git a/ruleset.xml b/ruleset.xml index dc2da08..1d4dc3b 100644 --- a/ruleset.xml +++ b/ruleset.xml @@ -1,78 +1,6 @@ - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/ActionHandler.php b/src/ActionHandler.php index 7041562..c1e6d50 100644 --- a/src/ActionHandler.php +++ b/src/ActionHandler.php @@ -7,11 +7,10 @@ interface ActionHandler { - /** - * @param RequestInterface $request + * @param RequestInterface $request * @param ResponseInterface $response - * @param array $arguments + * @param mixed[] $arguments * @return ResponseInterface */ public function __invoke( @@ -19,5 +18,4 @@ public function __invoke( ResponseInterface $response, array $arguments = [] ): ResponseInterface; - } diff --git a/src/Controller.php b/src/Controller.php index de5b219..9d11345 100644 --- a/src/Controller.php +++ b/src/Controller.php @@ -12,17 +12,18 @@ */ abstract class Controller extends Container { - public function beforeAction(RequestInterface $request, ResponseInterface $response): void { // intentionally - this method is empty in default } + public function afterAction(RequestInterface $request, ResponseInterface $response): void { // intentionally - this method is empty in default } + public function middleware(RequestInterface $request, ResponseInterface $response, Route $next): ResponseInterface { $this->beforeAction($request, $response); @@ -32,5 +33,4 @@ public function middleware(RequestInterface $request, ResponseInterface $respons return $response; } - } diff --git a/src/DI/SlimApiExtension.php b/src/DI/SlimApiExtension.php index 396f610..1c40f75 100644 --- a/src/DI/SlimApiExtension.php +++ b/src/DI/SlimApiExtension.php @@ -7,15 +7,15 @@ final class SlimApiExtension extends CompilerExtension { - /** - * @var array + * @var mixed[] */ private $defaults = [ - 'apiDefinitionKey' => 'api', + 'apiDefinitionKey' => 'api', 'slimConfiguration' => [], ]; + public function loadConfiguration(): void { $this->validateConfig($this->defaults); @@ -23,5 +23,4 @@ public function loadConfiguration(): void $builder->addDefinition($this->prefix('slimApi.factory')) ->setFactory(SlimApplicationFactory::class, [$this->config]); } - } diff --git a/src/ErrorHandler.php b/src/ErrorHandler.php index cdc7662..98d8e83 100644 --- a/src/ErrorHandler.php +++ b/src/ErrorHandler.php @@ -8,11 +8,9 @@ interface ErrorHandler { - public function __invoke( RequestInterface $request, ResponseInterface $response, ?Throwable $e = null ): ResponseInterface; - } diff --git a/src/Middleware.php b/src/Middleware.php index 0c3039d..dbb371b 100644 --- a/src/Middleware.php +++ b/src/Middleware.php @@ -7,7 +7,5 @@ interface Middleware { - public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface; - } diff --git a/src/MissingApiArgumentException.php b/src/MissingApiArgumentException.php index b00795e..0bde0bb 100644 --- a/src/MissingApiArgumentException.php +++ b/src/MissingApiArgumentException.php @@ -6,5 +6,4 @@ final class MissingApiArgumentException extends Exception implements RequestException { - } diff --git a/src/Request/Request.php b/src/Request/Request.php index 72350ef..580ea66 100644 --- a/src/Request/Request.php +++ b/src/Request/Request.php @@ -13,10 +13,11 @@ use Psr\Http\Message\UriInterface; use Slim\Route; use stdClass; +use function array_key_exists; +use function sprintf; final class Request implements RequestInterface { - /** * @var stdClass|null */ @@ -27,13 +28,17 @@ final class Request implements RequestInterface */ private $request; + public function __construct(ServerRequestInterface $request) { $this->request = $request; } + /** - * @inheritdoc + * @param string $key + * @param string|int|null $default + * @return string|integer|null */ public function getQueryParam(string $key, $default = null) { @@ -47,10 +52,7 @@ public function getQueryParam(string $key, $default = null) return $result; } - /** - * @inheritdoc - * @throws MissingApiArgumentException - */ + public function getRequiredArgument(string $name): string { $arguments = $this->request->getAttributes(); @@ -59,30 +61,33 @@ public function getRequiredArgument(string $name): string $value = Strings::trim($value); if ($value === '') { - throw new MissingApiArgumentException(\sprintf('Missing "%s" argument', $name)); + throw new MissingApiArgumentException(sprintf('Missing "%s" argument', $name)); } return $value; } + /** - * @inheritdoc - * @throws MissingApiArgumentException + * @param string $name + * @return mixed[]|stdClass|string */ public function getField(string $name) { $body = $this->getDecodedJsonFromBody(); if (!$this->hasField($name)) { - throw new MissingApiArgumentException(\sprintf('Field "%s" is missing in request body', $name)); + throw new MissingApiArgumentException(sprintf('Field "%s" is missing in request body', $name)); } - return $body->$name; + return ((array)$body)[$name]; } + /** - * @inheritdoc - * @throws MissingApiArgumentException + * @param string $name + * @param int|string|null $default + * @return mixed[]|stdClass|string|integer|null */ public function getOptionalField($name, $default = null) { @@ -91,217 +96,267 @@ public function getOptionalField($name, $default = null) : $default; } + /** - * @inheritdoc + * @param string $name + * @return boolean */ public function hasField($name): bool { - return \array_key_exists($name, (array)$this->getDecodedJsonFromBody()); + return array_key_exists($name, (array)$this->getDecodedJsonFromBody()); } + public function getProtocolVersion(): string { return $this->request->getProtocolVersion(); } + /** - * @inheritDoc + * @param string $version + * @return static */ - public function withProtocolVersion($version) + public function withProtocolVersion($version): self { return new static($this->request->withProtocolVersion($version)); } + /** - * @inheritDoc + * @return string[][] */ public function getHeaders(): array { return $this->request->getHeaders(); } + /** - * @inheritDoc + * @param string $name + * @return boolean */ public function hasHeader($name): bool { return $this->request->hasHeader($name); } + /** - * @inheritDoc + * @param string $name + * @return string[] */ public function getHeader($name): array { return $this->request->getHeader($name); } + /** - * @inheritDoc + * @param string $name + * @return string */ public function getHeaderLine($name): string { return $this->request->getHeaderLine($name); } + /** - * @inheritDoc + * @param string $name + * @param string|string[] $value + * @return static */ - public function withHeader($name, $value) + public function withHeader($name, $value): self { return new static($this->request->withHeader($name, $value)); } + /** - * @inheritDoc + * @param string $name + * @param string|string[] $value + * @return static */ - public function withAddedHeader($name, $value) + public function withAddedHeader($name, $value): self { return new static($this->request->withAddedHeader($name, $value)); } + /** - * @inheritDoc + * @param string $name + * @return static */ - public function withoutHeader($name) + public function withoutHeader($name): self { return new static($this->request->withoutHeader($name)); } + public function getBody(): StreamInterface { return $this->request->getBody(); } + /** - * @inheritDoc + * @param StreamInterface $body + * @return static */ - public function withBody(StreamInterface $body) + public function withBody(StreamInterface $body): self { return new static($this->request->withBody($body)); } + public function getRequestTarget(): string { return $this->request->getRequestTarget(); } + /** - * @inheritDoc + * @param mixed $requestTarget + * @return static */ - public function withRequestTarget($requestTarget) + public function withRequestTarget($requestTarget): self { return new static($this->request->withRequestTarget($requestTarget)); } + public function getMethod(): string { return $this->request->getMethod(); } + /** - * @inheritDoc + * @param string $method + * @return static */ - public function withMethod($method) + public function withMethod($method): self { return new static($this->request->withMethod($method)); } + public function getUri(): UriInterface { return $this->request->getUri(); } + /** - * @inheritDoc + * @param UriInterface $uri + * @param bool $preserveHost + * @return static */ - public function withUri(UriInterface $uri, $preserveHost = false) + public function withUri(UriInterface $uri, $preserveHost = false): self { return new static($this->request->withUri($uri, $preserveHost)); } + /** - * @inheritDoc + * @return mixed[] */ public function getServerParams(): array { return $this->request->getServerParams(); } + /** - * @inheritDoc + * @return mixed[] */ public function getCookieParams(): array { return $this->request->getCookieParams(); } + /** - * @inheritDoc + * @param mixed[] $cookies + * @return static */ - public function withCookieParams(array $cookies) + public function withCookieParams(array $cookies): self { return new static($this->request->withCookieParams($cookies)); } + /** - * @inheritDoc + * @return mixed[] */ public function getQueryParams(): array { return $this->request->getQueryParams(); } + /** - * @inheritDoc + * @param mixed[] $query + * @return static */ - public function withQueryParams(array $query) + public function withQueryParams(array $query): self { return new static($this->request->withQueryParams($query)); } + /** - * @inheritDoc + * @return mixed[] */ public function getUploadedFiles(): array { return $this->request->getUploadedFiles(); } + /** - * @inheritDoc + * @param mixed[] $uploadedFiles + * @return static */ public function withUploadedFiles(array $uploadedFiles) { return new static($this->request->withUploadedFiles($uploadedFiles)); } + /** - * @inheritDoc + * @return mixed[]|object|null */ public function getParsedBody() { return $this->request->getParsedBody(); } + /** - * @inheritDoc + * @param mixed[]|object|null $data + * @return static */ - public function withParsedBody($data) + public function withParsedBody($data): self { return new static($this->request->withParsedBody($data)); } + /** - * @inheritDoc + * @return mixed[] */ public function getAttributes(): array { return $this->request->getAttributes(); } + /** - * @inheritDoc + * @param string $name + * @param mixed $default + * @return mixed */ public function getAttribute($name, $default = null) { @@ -318,24 +373,30 @@ public function getAttribute($name, $default = null) return $value; } + /** - * @inheritDoc + * @param string $name + * @param mixed $value + * @return static */ - public function withAttribute($name, $value) + public function withAttribute($name, $value): self { return new static($this->request->withAttribute($name, $value)); } + /** - * @inheritDoc + * @param string $name + * @return static */ - public function withoutAttribute($name) + public function withoutAttribute($name): self { return new static($this->request->withoutAttribute($name)); } + /** - * @inheritdoc + * @return mixed[]|object */ public function getDecodedJsonFromBody() { @@ -347,21 +408,21 @@ public function getDecodedJsonFromBody() return $this->decodedJsonFromBody; } + public function getDateTimeQueryParam(string $field): DateTimeImmutable { - $datetimeParam = $this->getQueryParam($field); + $datetimeParam = (string)$this->getQueryParam($field); - if ($datetimeParam === null) { - throw new LogicException(\sprintf('Could not find %s in request\'s params', $field)); + if ($datetimeParam === '') { + throw new LogicException(sprintf('Could not find %s in request\'s params', $field)); } $datetime = DateTimeImmutable::createFromFormat(DateTime::ATOM, $datetimeParam); if ($datetime === false || $datetime->format(DateTime::ATOM) !== $datetimeParam) { - throw new LogicException(\sprintf('Could not parse %s as datetime', $field)); + throw new LogicException(sprintf('Could not parse %s as datetime', $field)); } return $datetime; } - } diff --git a/src/Request/RequestInterface.php b/src/Request/RequestInterface.php index e64293e..0336aa6 100644 --- a/src/Request/RequestInterface.php +++ b/src/Request/RequestInterface.php @@ -8,36 +8,40 @@ interface RequestInterface extends ServerRequestInterface { - /** - * @param string $key - * @param mixed $default - * @return mixed|null + * @param string $key + * @param string|int|null $default + * @return string|integer|null */ public function getQueryParam(string $key, $default = null); + public function getRequiredArgument(string $name): string; + /** * @param string $name - * @return array|stdClass|string + * @return mixed[]|stdClass|string */ public function getField(string $name); + /** - * @param string $name - * @param mixed $default - * @return mixed + * @param string $name + * @param string|int|null $default + * @return mixed[]|stdClass|string|integer|null */ public function getOptionalField(string $name, $default = null); + public function hasField(string $name): bool; + /** - * @return mixed + * @return mixed[]|object */ public function getDecodedJsonFromBody(); - public function getDateTimeQueryParam(string $key): DateTimeImmutable; + public function getDateTimeQueryParam(string $key): DateTimeImmutable; } diff --git a/src/RequestException.php b/src/RequestException.php index e79d4ab..b472fd5 100644 --- a/src/RequestException.php +++ b/src/RequestException.php @@ -4,5 +4,4 @@ interface RequestException { - } diff --git a/src/Response/Response.php b/src/Response/Response.php index 55beebb..9f26fa8 100644 --- a/src/Response/Response.php +++ b/src/Response/Response.php @@ -7,123 +7,152 @@ final class Response implements ResponseInterface { - /** * @var SlimResponse */ private $slimResponse; + public function __construct(SlimResponse $slimResponse) { $this->slimResponse = $slimResponse; } + public function getProtocolVersion(): string { return $this->slimResponse->getProtocolVersion(); } + /** - * @inheritdoc + * @param string $version + * @return static */ - public function withProtocolVersion($version) + public function withProtocolVersion($version): self { return new static($this->slimResponse->withProtocolVersion($version)); } + /** - * @inheritdoc + * @return string[][] */ public function getHeaders(): array { return $this->slimResponse->getHeaders(); } + /** - * @inheritdoc + * @param string $name + * @return boolean */ public function hasHeader($name): bool { return $this->slimResponse->hasHeader($name); } + /** - * @inheritdoc + * @param string $name + * @return string[] */ public function getHeader($name): array { return $this->slimResponse->getHeader($name); } + /** - * @inheritdoc + * @param string $name + * @return string */ public function getHeaderLine($name): string { return $this->slimResponse->getHeaderLine($name); } + /** - * @inheritdoc + * @param string $name + * @param string|string[] $value + * @return static */ - public function withHeader($name, $value) + public function withHeader($name, $value): self { return new static($this->slimResponse->withHeader($name, $value)); } + /** - * @inheritdoc + * @param string $name + * @param string|string[] $value + * @return static */ - public function withAddedHeader($name, $value) + public function withAddedHeader($name, $value): self { return new static($this->slimResponse->withAddedHeader($name, $value)); } + /** - * @inheritdoc + * @param string $name + * @return static */ - public function withoutHeader($name) + public function withoutHeader($name): self { return new static($this->slimResponse->withoutHeader($name)); } + public function getBody(): StreamInterface { return $this->slimResponse->getBody(); } + /** - * @inheritdoc + * @param StreamInterface $body + * @return static */ - public function withBody(StreamInterface $body) + public function withBody(StreamInterface $body): self { return new static($this->slimResponse->withBody($body)); } + public function getStatusCode(): int { return $this->slimResponse->getStatusCode(); } + /** - * @inheritdoc + * @param int $code + * @param string $reasonPhrase + * @return static */ - public function withStatus($code, $reasonPhrase = '') + public function withStatus($code, $reasonPhrase = ''): self { return new static($this->slimResponse->withStatus($code, $reasonPhrase)); } + /** - * @inheritdoc + * @param mixed[]|object $data + * @param int|null $status + * @param int $encodingOptions + * @return static */ - public function withJson($data, $status = null, $encodingOptions = 0) + public function withJson($data, ?int $status = null, int $encodingOptions = 0): self { return new static($this->slimResponse->withJson($data, $status, $encodingOptions)); } + public function getReasonPhrase(): string { return $this->slimResponse->getReasonPhrase(); } - } diff --git a/src/Response/ResponseInterface.php b/src/Response/ResponseInterface.php index 1c84583..9be14fc 100644 --- a/src/Response/ResponseInterface.php +++ b/src/Response/ResponseInterface.php @@ -6,13 +6,11 @@ interface ResponseInterface extends PsrResponseInterface { - /** - * @param mixed $data - * @param int|null $status - * @param int $encodingOptions + * @param mixed[]|object $data + * @param int|null $status + * @param int $encodingOptions * @return static */ - public function withJson($data, $status = null, $encodingOptions = 0); - + public function withJson($data, ?int $status = null, int $encodingOptions = 0); } diff --git a/src/SlimApp.php b/src/SlimApp.php index 5f24fe6..a20bf5b 100644 --- a/src/SlimApp.php +++ b/src/SlimApp.php @@ -6,13 +6,10 @@ use BrandEmbassy\Slim\Response\Response; use Psr\Http\Message\ResponseInterface; use Slim\App; +use function reset; class SlimApp extends App { - - /** - * @inheritdoc - */ public function run($silent = false): ResponseInterface { $request = new Request($this->getContainer()->get('request')); @@ -20,7 +17,7 @@ public function run($silent = false): ResponseInterface $response = $this->process($request, $response); $contentTypes = $response->getHeader('Content-Type'); - $contentType = \reset($contentTypes); + $contentType = reset($contentTypes); if ($contentType === 'text/html; charset=UTF-8' && $response->getBody()->getSize() === 0) { $response = $response->withHeader('Content-Type', 'text/plain; charset=UTF-8'); @@ -32,5 +29,4 @@ public function run($silent = false): ResponseInterface return $response; } - } diff --git a/src/SlimApplicationFactory.php b/src/SlimApplicationFactory.php index 7b214cd..c5530c1 100644 --- a/src/SlimApplicationFactory.php +++ b/src/SlimApplicationFactory.php @@ -8,12 +8,16 @@ use LogicException; use Nette\DI\Container; use Slim\Collection; +use Throwable; +use function gettype; +use function is_array; +use function sprintf; +use function trim; final class SlimApplicationFactory { - /** - * @var array + * @var mixed[] */ private $configuration; @@ -23,12 +27,13 @@ final class SlimApplicationFactory private $container; /** - * @var Middleware[] + * @var array */ private $beforeRoutesMiddlewares; + /** - * @param array $configuration + * @param mixed[] $configuration * @param Container $container */ public function __construct(array $configuration, Container $container) @@ -38,6 +43,7 @@ public function __construct(array $configuration, Container $container) $this->beforeRoutesMiddlewares = []; } + public function create(): SlimApp { $app = new SlimApp($this->configuration['slimConfiguration']); @@ -72,16 +78,17 @@ public function create(): SlimApp return $app; } + /** * @param string $configurationCode - * @return array + * @return mixed[] */ private function getConfiguration(string $configurationCode): array { $configuration = $this->container->getParameters()[$configurationCode]; - if (!\is_array($configuration)) { - throw new LogicException(\sprintf('Missing %s configuration', $configurationCode)); + if (!is_array($configuration)) { + throw new LogicException(sprintf('Missing %s configuration', $configurationCode)); } $this->validateConfiguration($configuration, $configurationCode, 'routes', 'array'); @@ -93,11 +100,12 @@ private function getConfiguration(string $configurationCode): array return $configuration; } + /** - * @param array $configuration - * @param string $configurationCode - * @param string $name - * @param string $type + * @param mixed[] $configuration + * @param string $configurationCode + * @param string $name + * @param string $type */ private function validateConfiguration( array $configuration, @@ -105,19 +113,20 @@ private function validateConfiguration( string $name, string $type ): void { - if (!isset($configuration[$name]) || \gettype($configuration[$name]) !== $type) { + if (!isset($configuration[$name]) || gettype($configuration[$name]) !== $type) { throw new LogicException( - \sprintf( + sprintf( 'Missing or empty %s.%s configuration (has to be %s, but is %s)', $configurationCode, $name, $type, - \gettype($configuration[$name] ?? null) + gettype($configuration[$name] ?? null) ) ); } } + /** * @param string $serviceName * @return Closure @@ -136,18 +145,20 @@ private function getServiceProvider(string $serviceName): callable }; } + private function removeDefaultSlimErrorHandlers(SlimApp $app): void { $app->getContainer()['phpErrorHandler'] = static function () { - return static function (RequestInterface $request, ResponseInterface $response, \Throwable $e): void { + return static function (RequestInterface $request, ResponseInterface $response, Throwable $e): void { throw $e; }; }; } + /** * @param SlimApp $app - * @param array $handlers + * @param mixed[] $handlers */ private function registerHandlers(SlimApp $app, array $handlers): void { @@ -156,6 +167,7 @@ private function registerHandlers(SlimApp $app, array $handlers): void } } + private function registerServiceIntoContainer(SlimApp $app, string $serviceName): void { if (!$app->getContainer()->has($serviceName)) { @@ -163,10 +175,11 @@ private function registerServiceIntoContainer(SlimApp $app, string $serviceName) } } + /** * @param SlimApp $app - * @param array $api - * @param string $apiName + * @param mixed[] $api + * @param string $apiName */ private function registerApis(SlimApp $app, array $api, string $apiName): void { @@ -175,11 +188,12 @@ private function registerApis(SlimApp $app, array $api, string $apiName): void } } + /** * @param SlimApp $app - * @param string $apiName - * @param string $version - * @param array $routes + * @param string $apiName + * @param string $version + * @param mixed[] $routes */ private function registerApi(SlimApp $app, string $apiName, string $version, array $routes): void { @@ -194,11 +208,12 @@ private function registerApi(SlimApp $app, string $apiName, string $version, arr } } + /** * @deprecated Do not use Controllers, use Invokable Action classes (use MiddleWareInterface) * @param SlimApp $app - * @param string $urlPattern - * @param array $routeData + * @param string $urlPattern + * @param mixed[] $routeData */ private function registerControllerRoute(SlimApp $app, string $urlPattern, array $routeData): void { @@ -206,14 +221,15 @@ private function registerControllerRoute(SlimApp $app, string $urlPattern, array foreach ($routeData['methods'] as $method => $action) { $app->map([$method], $urlPattern, $routeData['service'] . ':' . $action) - ->add($routeData['service'] . ':' . 'middleware'); + ->add($routeData['service'] . ':middleware'); } } + /** * @param SlimApp $app - * @param array $routeData - * @param string $urlPattern + * @param mixed[] $routeData + * @param string $urlPattern */ private function registerInvokableActionRoutes(SlimApp $app, array $routeData, string $urlPattern): void { @@ -237,11 +253,12 @@ private function registerInvokableActionRoutes(SlimApp $app, array $routeData, s } } + private function createUrlPattern(string $apiName, string $version, string $routeName): string { - $apiName = \trim($apiName, '/'); - $version = \trim($version, '/'); - $routeName = \trim($routeName, '/'); + $apiName = trim($apiName, '/'); + $version = trim($version, '/'); + $routeName = trim($routeName, '/'); if ($version !== '') { $version = '/' . $version; @@ -258,15 +275,17 @@ private function createUrlPattern(string $apiName, string $version, string $rout return $apiName . $version . $routeName; } + private function registerBeforeRequestMiddleware(SlimApp $app, string $middleware): void { $this->registerServiceIntoContainer($app, $middleware); $app->add($middleware); } + /** * @param SlimApp $app - * @param array $configuration + * @param mixed[] $configuration */ private function registerBeforeRouteMiddlewares(SlimApp $app, array $configuration): void { @@ -277,5 +296,4 @@ private function registerBeforeRouteMiddlewares(SlimApp $app, array $configurati } } } - } diff --git a/tests/Dummy/ApiErrorHandler.php b/tests/Dummy/ApiErrorHandler.php index 80520c8..402d02a 100644 --- a/tests/Dummy/ApiErrorHandler.php +++ b/tests/Dummy/ApiErrorHandler.php @@ -9,7 +9,6 @@ final class ApiErrorHandler implements ErrorHandler { - public function __invoke( RequestInterface $request, ResponseInterface $response, @@ -21,5 +20,4 @@ public function __invoke( return $response->withJson(['error' => $error], 500); } - } diff --git a/tests/Dummy/BeforeRequestMiddleware.php b/tests/Dummy/BeforeRequestMiddleware.php index 26233c8..7522249 100644 --- a/tests/Dummy/BeforeRequestMiddleware.php +++ b/tests/Dummy/BeforeRequestMiddleware.php @@ -8,7 +8,6 @@ class BeforeRequestMiddleware implements Middleware { - public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface { $response = $response->withAddedHeader( @@ -18,5 +17,4 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, return $next($request, $response); } - } diff --git a/tests/Dummy/BeforeRouteMiddleware.php b/tests/Dummy/BeforeRouteMiddleware.php index 7499223..dde11be 100644 --- a/tests/Dummy/BeforeRouteMiddleware.php +++ b/tests/Dummy/BeforeRouteMiddleware.php @@ -8,7 +8,6 @@ class BeforeRouteMiddleware implements Middleware { - public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface { $response = $response->withAddedHeader( @@ -18,5 +17,4 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, return $next($request, $response); } - } diff --git a/tests/Dummy/CreateChannelAction.php b/tests/Dummy/CreateChannelAction.php index e936b67..86eae2d 100644 --- a/tests/Dummy/CreateChannelAction.php +++ b/tests/Dummy/CreateChannelAction.php @@ -8,7 +8,6 @@ final class CreateChannelAction implements ActionHandler { - /** * @inheritdoc */ @@ -19,5 +18,4 @@ public function __invoke( ): ResponseInterface { return $response->withJson(['channelId' => 'fb_1234'], 201); } - } diff --git a/tests/Dummy/ErroringAction.php b/tests/Dummy/ErroringAction.php index f560fce..43b53de 100644 --- a/tests/Dummy/ErroringAction.php +++ b/tests/Dummy/ErroringAction.php @@ -9,7 +9,13 @@ final class ErroringAction implements ActionHandler { - + // phpcs:disable + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @param mixed[] $arguments + * @return ResponseInterface + */ public function __invoke( RequestInterface $request, ResponseInterface $response, @@ -17,5 +23,5 @@ public function __invoke( ): ResponseInterface { throw new LogicException('Error or not to error, that\'s the question!'); } - + // phpcs:enable } diff --git a/tests/Dummy/GetHelloWorldAction.php b/tests/Dummy/GetHelloWorldAction.php index 51bec9e..0cd92f3 100644 --- a/tests/Dummy/GetHelloWorldAction.php +++ b/tests/Dummy/GetHelloWorldAction.php @@ -8,13 +8,17 @@ final class GetHelloWorldAction implements ActionHandler { - + /** + * @param RequestInterface $request + * @param ResponseInterface $response + * @param mixed[] $arguments + * @return ResponseInterface + */ public function __invoke( RequestInterface $request, ResponseInterface $response, array $arguments = [] ): ResponseInterface { - return $response->withJson('Hello World'); + return $response->withJson(['Hello World']); } - } diff --git a/tests/Dummy/GoldenKeyAuthMiddleware.php b/tests/Dummy/GoldenKeyAuthMiddleware.php index e4ccf30..c3f5073 100644 --- a/tests/Dummy/GoldenKeyAuthMiddleware.php +++ b/tests/Dummy/GoldenKeyAuthMiddleware.php @@ -5,19 +5,14 @@ use BrandEmbassy\Slim\Middleware; use BrandEmbassy\Slim\Request\RequestInterface; use BrandEmbassy\Slim\Response\ResponseInterface; -use Exception; +use function reset; final class GoldenKeyAuthMiddleware implements Middleware { - - /** - * @inheritdoc - * @throws Exception - */ public function __invoke(RequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface { $headerData = $request->getHeader('goldenKey'); - $token = \reset($headerData); + $token = reset($headerData); $token = $token !== false ? $token : ''; @@ -28,5 +23,4 @@ public function __invoke(RequestInterface $request, ResponseInterface $response, return $next($request, $response); } - } diff --git a/tests/Dummy/NotAllowedHandler.php b/tests/Dummy/NotAllowedHandler.php index e3a848b..73fb851 100644 --- a/tests/Dummy/NotAllowedHandler.php +++ b/tests/Dummy/NotAllowedHandler.php @@ -10,10 +10,8 @@ */ final class NotAllowedHandler { - public function __invoke(RequestInterface $request, ResponseInterface $response): ResponseInterface { return $response->withJson(['error' => 'Dummy NotAllowedHandler here!'], 405); } - } diff --git a/tests/Dummy/NotFoundHandler.php b/tests/Dummy/NotFoundHandler.php index 8208e16..07c341f 100644 --- a/tests/Dummy/NotFoundHandler.php +++ b/tests/Dummy/NotFoundHandler.php @@ -9,7 +9,6 @@ final class NotFoundHandler implements ErrorHandler { - public function __invoke( RequestInterface $request, ResponseInterface $response, @@ -17,5 +16,4 @@ public function __invoke( ): ResponseInterface { return $response->withJson(['error' => 'Dummy NotFoundHandler here!'], 404); } - } diff --git a/tests/Request/RequestTest.php b/tests/Request/RequestTest.php index 54d181d..8fb8a76 100644 --- a/tests/Request/RequestTest.php +++ b/tests/Request/RequestTest.php @@ -14,13 +14,17 @@ use Slim\Http\Headers; use Slim\Http\Request as SlimRequest; use Slim\Http\Uri; +use function assert; +use function fopen; +use function is_resource; +use function sprintf; final class RequestTest extends TestCase { - private const PARAM_NAME = 'dateFrom'; private const DATE_TIME_STRING = '2017-06-10T01:00:00+01:00'; + public function testShouldDistinguishBetweenNullAndEmptyOption(): void { $request = $this->createDummyRequest(); @@ -30,6 +34,7 @@ public function testShouldDistinguishBetweenNullAndEmptyOption(): void self::assertTrue($request->hasField('thisIsGandalf')); } + public function testShouldRaiseExceptionForMissingRequiredField(): void { $request = $this->createDummyRequest(); @@ -39,6 +44,7 @@ public function testShouldRaiseExceptionForMissingRequiredField(): void $request->getField('nonExistingField'); } + public function testGettingDateTimeQueryParam(): void { $arguments = [self::PARAM_NAME => self::DATE_TIME_STRING]; @@ -50,10 +56,11 @@ public function testGettingDateTimeQueryParam(): void self::assertSame(self::DATE_TIME_STRING, $dateTime->format(DateTime::ATOM)); } + /** * @dataProvider getDataForInvalidDateTimeArgument - * @param string $logicExceptionMessage - * @param array $arguments + * @param string $logicExceptionMessage + * @param mixed[] $arguments */ public function testGettingDateTimeQueryParamThrowsExceptionIfInvalidArgument( string $logicExceptionMessage, @@ -67,25 +74,27 @@ public function testGettingDateTimeQueryParamThrowsExceptionIfInvalidArgument( $request->getDateTimeQueryParam(self::PARAM_NAME); } + /** - * @return array + * @return mixed[] */ public function getDataForInvalidDateTimeArgument(): array { return [ 'Missing from' => [ - \sprintf('Could not find %s in request\'s params', self::PARAM_NAME), + sprintf('Could not find %s in request\'s params', self::PARAM_NAME), [], ], 'Invalid from' => [ - \sprintf('Could not parse %s as datetime', self::PARAM_NAME), + sprintf('Could not parse %s as datetime', self::PARAM_NAME), [self::PARAM_NAME => '123456789'], ], ]; } + /** - * @param array $arguments + * @param mixed[] $arguments * @return MockInterface&SlimRequest */ private function createMockSlimRequest(array $arguments): MockInterface @@ -97,6 +106,7 @@ private function createMockSlimRequest(array $arguments): MockInterface return $mock; } + private function createRequest(StreamInterface $body): Request { $url = new Uri('https', 'example.com'); @@ -105,15 +115,15 @@ private function createRequest(StreamInterface $body): Request return new Request($slimRequest); } + private function createDummyRequest(): Request { - $resource = \fopen('php://temp', 'rb+'); - \assert(\is_resource($resource)); + $resource = fopen('php://temp', 'rb+'); + assert(is_resource($resource)); $body = new Body($resource); $body->write('{"thisIsNull": null, "thisIsGandalf": "gandalf"}'); $body->rewind(); return $this->createRequest($body); } - } diff --git a/tests/SlimApplicationFactoryTest.php b/tests/SlimApplicationFactoryTest.php index 639440f..c2d3628 100644 --- a/tests/SlimApplicationFactoryTest.php +++ b/tests/SlimApplicationFactoryTest.php @@ -16,24 +16,29 @@ use Slim\Http\Headers; use Slim\Http\Request as SlimRequest; use Slim\Http\Uri; +use function assert; +use function fopen; +use function is_resource; +use function md5; final class SlimApplicationFactoryTest extends TestCase { - public function testShouldPassSettingsToSlimContainer(): void { $app = $this->createSlimApp(); $settings = $app->getContainer()->get('settings'); - $this->assertSame('Dummy', $settings['myCustomOption']); + self::assertSame('Dummy', $settings['myCustomOption']); } + public function testShouldAllowEmptyErrorHandlers(): void { $this->createSlimApp(__DIR__ . '/configNoHandlers.neon'); $this->expectNotToPerformAssertions(); } + public function testHandledRouteForOmittedUrlParts(): void { $request = $this->createRequest('GET', '/app'); @@ -42,9 +47,10 @@ public function testHandledRouteForOmittedUrlParts(): void $response = $this->createSlimApp()->process($request, new Response(new \Slim\Http\Response())); self::assertEquals(200, $response->getStatusCode()); - self::assertEquals('"Hello World"', $this->getContents($response)); + self::assertEquals('["Hello World"]', $this->getContents($response)); } + public function testShouldBeHandledByNotFoundErrorHandler(): void { $request = $this->createRequest('POST', '/non-existing/path'); @@ -56,6 +62,7 @@ public function testShouldBeHandledByNotFoundErrorHandler(): void self::assertEquals('{"error":"Dummy NotFoundHandler here!"}', $this->getContents($response)); } + public function testShouldBeHandledByNotAllowedHandler(): void { $request = $this->createRequest('PATCH', '/new-api/2.0/channels'); @@ -67,6 +74,7 @@ public function testShouldBeHandledByNotAllowedHandler(): void self::assertEquals('{"error":"Dummy NotAllowedHandler here!"}', $this->getContents($response)); } + public function testShouldBeHandledByApiErrorHandler(): void { $request = $this->createRequest('POST', '/new-api/2.0/error'); @@ -78,6 +86,7 @@ public function testShouldBeHandledByApiErrorHandler(): void self::assertEquals('{"error":"Error or not to error, that\'s the question!"}', $this->getContents($response)); } + public function testShouldDenyRequestByAccessMiddleware(): void { $request = $this->createRequest('POST', '/new-api/2.0/channels'); @@ -89,6 +98,7 @@ public function testShouldDenyRequestByAccessMiddleware(): void self::assertEquals('{"error":"YOU SHALL NOT PASS!"}', $this->getContents($response)); } + public function testShouldAllowRequestByAccessMiddleware(): void { $request = $this->createRequest( @@ -104,6 +114,7 @@ public function testShouldAllowRequestByAccessMiddleware(): void self::assertEquals('{"channelId":"fb_1234"}', $this->getContents($response)); } + public function testShouldProcessBothGlobalMiddlewares(): void { $request = $this->createRequest('POST', '/new-api/2.0/channels'); @@ -122,6 +133,7 @@ public function testShouldProcessBothGlobalMiddlewares(): void ); } + public function testShouldProcessBeforeRequestMiddleware(): void { $request = $this->createRequest('POST', '/non-existing/path'); @@ -135,6 +147,7 @@ public function testShouldProcessBeforeRequestMiddleware(): void ); } + private function createContainer(string $configPath = __DIR__ . '/config.neon'): Container { $loader = new ContainerLoader(__DIR__ . '/temp', true); @@ -143,22 +156,23 @@ static function (Compiler $compiler) use ($configPath): void { $compiler->loadConfig($configPath); $compiler->addExtension('extensions', new ExtensionsExtension()); }, - \md5($configPath) + md5($configPath) ); return new $class(); } + /** - * @param string $requestMethod - * @param string $requestUrlPath - * @param string[] $headers + * @param string $requestMethod + * @param string $requestUrlPath + * @param array $headers * @return Request */ private function createRequest(string $requestMethod, string $requestUrlPath, array $headers = []): Request { - $body = \fopen('php://temp', 'rb+'); - \assert(\is_resource($body)); + $body = fopen('php://temp', 'rb+'); + assert(is_resource($body)); $slimRequest = new SlimRequest( $requestMethod, new Uri('http', 'api.be.com', 80, $requestUrlPath), @@ -171,6 +185,7 @@ private function createRequest(string $requestMethod, string $requestUrlPath, ar return new Request($slimRequest); } + private function createSlimApp(string $configPath = __DIR__ . '/config.neon'): App { /** @var SlimApplicationFactory $factory */ @@ -179,6 +194,7 @@ private function createSlimApp(string $configPath = __DIR__ . '/config.neon'): A return $factory->create(); } + private function getContents(ResponseInterface $response): string { $body = $response->getBody(); @@ -186,5 +202,4 @@ private function getContents(ResponseInterface $response): string return $body->getContents(); } - }