From 26c59c93f95f5758ca4459239a229a7bbf8bc7cb Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Sun, 18 Feb 2024 13:14:52 +0700 Subject: [PATCH 1/9] Decompose format function --- src/Helper/DefinitionHelper.php | 65 ++++++++++++++++++++ src/InvalidMiddlewareDefinitionException.php | 50 +-------------- 2 files changed, 67 insertions(+), 48 deletions(-) create mode 100644 src/Helper/DefinitionHelper.php diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php new file mode 100644 index 0000000..ea63e1e --- /dev/null +++ b/src/Helper/DefinitionHelper.php @@ -0,0 +1,65 @@ + $value) { + $items[] = (is_string($key) ? '"' . $key . '" => ' : '') . self::convertToString($value); + } + return '[' . implode(', ', $items) . (count($middlewareDefinition) > 2 ? ', ...' : '') . ']'; + } + + return self::convertToString($middlewareDefinition); + } + + private static function convertToString(mixed $value): string + { + if (is_string($value)) { + return '"' . $value . '"'; + } + + if (is_int($value) || is_float($value)) { + return (string) $value; + } + + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + + if ($value === null) { + return 'null'; + } + + if (is_object($value)) { + return $value::class; + } + + return gettype($value); + } +} diff --git a/src/InvalidMiddlewareDefinitionException.php b/src/InvalidMiddlewareDefinitionException.php index a2b85aa..444bb1c 100644 --- a/src/InvalidMiddlewareDefinitionException.php +++ b/src/InvalidMiddlewareDefinitionException.php @@ -9,6 +9,7 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Helpers\DefinitionValidator; use Yiisoft\FriendlyException\FriendlyExceptionInterface; +use Yiisoft\Middleware\Dispatcher\Helper\DefinitionHelper; use function array_slice; use function count; @@ -27,7 +28,7 @@ final class InvalidMiddlewareDefinitionException extends InvalidArgumentExceptio public function __construct( private mixed $definition ) { - $this->definitionString = $this->convertDefinitionToString($definition); + $this->definitionString = DefinitionHelper::convertDefinitionToString($definition); parent::__construct( 'Parameter should be either PSR middleware class name or a callable. Got ' . $this->definitionString . '.' @@ -190,51 +191,4 @@ private function isControllerWithNonExistAction(): bool && is_string($this->definition[0]) && class_exists($this->definition[0]); } - - private function convertDefinitionToString(mixed $middlewareDefinition): string - { - if (is_object($middlewareDefinition)) { - return 'an instance of "' . $middlewareDefinition::class . '"'; - } - - if (is_string($middlewareDefinition)) { - return '"' . $middlewareDefinition . '"'; - } - - if (is_array($middlewareDefinition)) { - $items = []; - /** @var mixed $value */ - foreach (array_slice($middlewareDefinition, 0, 2) as $key => $value) { - $items[] = (is_string($key) ? '"' . $key . '" => ' : '') . $this->convertToString($value); - } - return '[' . implode(', ', $items) . (count($middlewareDefinition) > 2 ? ', ...' : '') . ']'; - } - - return $this->convertToString($middlewareDefinition); - } - - private function convertToString(mixed $value): string - { - if (is_string($value)) { - return '"' . $value . '"'; - } - - if (is_int($value) || is_float($value)) { - return (string) $value; - } - - if (is_bool($value)) { - return $value ? 'true' : 'false'; - } - - if ($value === null) { - return 'null'; - } - - if (is_object($value)) { - return $value::class; - } - - return gettype($value); - } } From 64781dbfcff432c5adb3968b9d76740ee022f1f9 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Sun, 18 Feb 2024 13:24:08 +0700 Subject: [PATCH 2/9] Decompose assert functions --- src/Helper/DefinitionHelper.php | 44 ++++++++++++++++++++ src/InvalidMiddlewareDefinitionException.php | 35 ++-------------- 2 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index ea63e1e..0bcb54f 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -4,6 +4,8 @@ namespace Yiisoft\Middleware\Dispatcher\Helper; +use Yiisoft\Definitions\Helpers\DefinitionValidator; + use function array_slice; use function count; use function gettype; @@ -16,6 +18,48 @@ final class DefinitionHelper { + public static function isValidArrayDefinition(mixed $definition): bool + { + if (!is_array($definition)) { + return false; + } + try{ + DefinitionValidator::validateArrayDefinition($definition); + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * @psalm-assert-if-true string $definition + */ + public static function isStringNotClassName(mixed $definition): bool + { + return is_string($definition) + && !class_exists($definition); + } + + /** + * @psalm-assert-if-true class-string $definition + */ + public static function isNotMiddlewareClassName(mixed $definition): bool + { + return is_string($definition) + && class_exists($definition); + } + + /** + * @psalm-assert-if-true array{0:class-string,1:string} $definition + */ + public static function isControllerWithNonExistAction(mixed $definition): bool + { + return is_array($definition) + && array_keys($definition) === [0, 1] + && is_string($definition[0]) + && class_exists($definition[0]); + } + public static function convertDefinitionToString(mixed $middlewareDefinition): string { if (is_object($middlewareDefinition)) { diff --git a/src/InvalidMiddlewareDefinitionException.php b/src/InvalidMiddlewareDefinitionException.php index 444bb1c..96807d8 100644 --- a/src/InvalidMiddlewareDefinitionException.php +++ b/src/InvalidMiddlewareDefinitionException.php @@ -109,7 +109,7 @@ static function (): MiddlewareInterface { private function generateSuggestion(): ?string { - if ($this->isControllerWithNonExistAction()) { + if (DefinitionHelper::isControllerWithNonExistAction($this->definition)) { return <<definition[0]}` exists, but does not contain method `{$this->definition[1]}()`. @@ -124,7 +124,7 @@ public function {$this->definition[1]}(): ResponseInterface SOLUTION; } - if ($this->isNotMiddlewareClassName()) { + if (DefinitionHelper::isNotMiddlewareClassName($this->definition)) { return sprintf( 'Class `%s` exists, but does not implement `%s`.', $this->definition, @@ -132,7 +132,7 @@ public function {$this->definition[1]}(): ResponseInterface ); } - if ($this->isStringNotClassName()) { + if (DefinitionHelper::isStringNotClassName($this->definition)) { return sprintf( 'Class `%s` not found. It may be needed to install a package with this middleware.', $this->definition @@ -162,33 +162,4 @@ public function {$this->definition[1]}(): ResponseInterface return null; } - - /** - * @psalm-assert-if-true string $this->definition - */ - private function isStringNotClassName(): bool - { - return is_string($this->definition) - && !class_exists($this->definition); - } - - /** - * @psalm-assert-if-true class-string $this->definition - */ - private function isNotMiddlewareClassName(): bool - { - return is_string($this->definition) - && class_exists($this->definition); - } - - /** - * @psalm-assert-if-true array{0:class-string,1:string} $this->definition - */ - private function isControllerWithNonExistAction(): bool - { - return is_array($this->definition) - && array_keys($this->definition) === [0, 1] - && is_string($this->definition[0]) - && class_exists($this->definition[0]); - } } From 3e8bf04e6f5d2c3406374d19ff2d541333ac841b Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Sun, 18 Feb 2024 14:14:51 +0700 Subject: [PATCH 3/9] Specify return type error --- .../InvalidMiddlewareReturnTypeException.php | 175 ++++++++++++++++++ src/Helper/DefinitionHelper.php | 2 +- src/Helper/ResponseHelper.php | 57 ++++++ src/MiddlewareFactory.php | 5 +- ...validMiddlewareReturnTypeExceptionTest.php | 126 +++++++++++++ tests/Helper/ResponseHelperTest.php | 60 ++++++ ...validMiddlewareDefinitionExceptionTest.php | 2 +- tests/MiddlewareFactoryTest.php | 15 +- 8 files changed, 436 insertions(+), 6 deletions(-) create mode 100644 src/Exception/InvalidMiddlewareReturnTypeException.php create mode 100644 src/Helper/ResponseHelper.php create mode 100644 tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php create mode 100644 tests/Helper/ResponseHelperTest.php diff --git a/src/Exception/InvalidMiddlewareReturnTypeException.php b/src/Exception/InvalidMiddlewareReturnTypeException.php new file mode 100644 index 0000000..6403baa --- /dev/null +++ b/src/Exception/InvalidMiddlewareReturnTypeException.php @@ -0,0 +1,175 @@ +definitionString = DefinitionHelper::convertDefinitionToString($definition); + + parent::__construct( + sprintf( + 'Middleware %s must return an instance of `%s` or `%s`, %s returned.', + $this->definitionString, + MiddlewareInterface::class, + ResponseInterface::class, + ResponseHelper::convertToString($this->result) + ) + ); + } + + public function getName(): string + { + return sprintf('Invalid middleware result type %s', get_debug_type($this->result)); + } + + public function getSolution(): ?string + { + $solution = [ + <<definitionString}` + SOLUTION + ]; + + $suggestion = $this->generateSuggestion(); + if ($suggestion !== null) { + $solution[] = '## Suggestion'; + $solution[] = $suggestion; + } + + $solution[] = << MyMiddleware::class, + '__construct()' => [ + 'someVar' => 42, + ], + ] + ``` + + Closure that returns `ResponseInterface`: + + ```php + static function (): ResponseInterface { + return new Response(418); + }, + ``` + + Closure that returns `MiddlewareInterface`: + + ```php + static function (): MiddlewareInterface { + return new TestMiddleware(); + } + ``` + + Action in controller: + + ```php + [App\Backend\UserController::class, 'index'] + ``` + + ## Related links + + - [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition) + - [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php) + SOLUTION; + + return implode("\n\n", $solution); + } + + private function generateSuggestion(): ?string + { + if (DefinitionHelper::isControllerWithNonExistAction($this->definition)) { + return <<definition[0]}` exists, but does not contain method `{$this->definition[1]}()`. + + Try adding `{$this->definition[1]}()` action to `{$this->definition[0]}` controller: + + ```php + public function {$this->definition[1]}(): ResponseInterface + { + // TODO: Implement your action + } + ``` + SOLUTION; + } + + if (DefinitionHelper::isNotMiddlewareClassName($this->definition)) { + return sprintf( + 'Class `%s` exists, but does not implement `%s`.', + $this->definition, + MiddlewareInterface::class + ); + } + + if (DefinitionHelper::isStringNotClassName($this->definition)) { + return sprintf( + 'Class `%s` not found. It may be needed to install a package with this middleware.', + $this->definition + ); + } + + if (is_array($this->definition)) { + try { + DefinitionValidator::validateArrayDefinition($this->definition); + } catch (InvalidConfigException $e) { + return <<getMessage()} + ``` + SOLUTION; + } + + /** @psalm-suppress MixedArgument In valid array definition element "class" always is string */ + return sprintf( + 'Array definition is valid, class `%s` exists, but does not implement `%s`.', + $this->definition['class'], + MiddlewareInterface::class + ); + } + + return null; + } +} diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index 0bcb54f..096defd 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -63,7 +63,7 @@ public static function isControllerWithNonExistAction(mixed $definition): bool public static function convertDefinitionToString(mixed $middlewareDefinition): string { if (is_object($middlewareDefinition)) { - return 'an instance of "' . $middlewareDefinition::class . '"'; + return 'an instance of `' . $middlewareDefinition::class . '`'; } if (is_string($middlewareDefinition)) { diff --git a/src/Helper/ResponseHelper.php b/src/Helper/ResponseHelper.php new file mode 100644 index 0000000..63f066e --- /dev/null +++ b/src/Helper/ResponseHelper.php @@ -0,0 +1,57 @@ + ...', $key); + } + } else { + $items[] = '...'; + } + return sprintf( + '"[%s]" (array of %d%s)', + implode(', ', $items), + $count = count($response), + $count === 1 ? ' element' : ' elements' + ); + } + if (is_array($response)) { + return sprintf('"%s" (array)', print_r($response, true)); + } + if (is_bool($response)) { + return sprintf('"%s" (bool)', $response ? 'true' : 'false'); + } + + if (is_scalar($response)) { + return sprintf( + '"%s" (%s)', + $response, + match (true) { + is_int($response) => 'int', + is_float($response) => 'float', + default => gettype($response) + } + ); + } + } +} diff --git a/src/MiddlewareFactory.php b/src/MiddlewareFactory.php index 75c44ac..c9f931c 100644 --- a/src/MiddlewareFactory.php +++ b/src/MiddlewareFactory.php @@ -17,6 +17,7 @@ use Yiisoft\Definitions\Exception\InvalidConfigException; use Yiisoft\Definitions\Helpers\DefinitionValidator; use Yiisoft\Injector\Injector; +use Yiisoft\Middleware\Dispatcher\Exception\InvalidMiddlewareReturnTypeException; use function in_array; use function is_array; @@ -216,7 +217,7 @@ public function process( return $response->process($request, $handler); } - throw new InvalidMiddlewareDefinitionException($this->callback); + throw new InvalidMiddlewareReturnTypeException($this->callback, $response); } public function __debugInfo(): array @@ -273,7 +274,7 @@ public function process( return $response; } - throw new InvalidMiddlewareDefinitionException([$this->class, $this->method]); + throw new InvalidMiddlewareReturnTypeException([$this->class, $this->method], $response); } public function __debugInfo() diff --git a/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php new file mode 100644 index 0000000..37c29b6 --- /dev/null +++ b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php @@ -0,0 +1,126 @@ + TestController::class, 'index'], + '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', + null, + ], + [ + ['object' => TestController::class, 'index'], + '["object" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', + 'You may have an error in array definition. Array definition validation result', + ], + [ + ['class' => TestController::class], + '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"]', + 'Array definition is valid, ' . + 'class `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` exists, ' . + 'but does not implement `Psr\Http\Server\MiddlewareInterface`.', + ], + ]; + } + + /** + * @dataProvider dataBase + */ + public function testBase(mixed $definition, string $expectedMessage, ?string $expectedSolution): void + { + $exception = new InvalidMiddlewareDefinitionException($definition); + self::assertStringEndsWith('. Got ' . $expectedMessage . '.', $exception->getMessage()); + if ($expectedSolution !== null) { + self::assertStringContainsString($expectedSolution, $exception->getSolution()); + } + } + + public function dataInvalidReturnType(): array + { + return [ + [fn() => 42, 42], + [fn() => [new stdClass()], new stdClass()], + [fn() => true, true], + [fn() => false, false], + [ + fn() => ['class' => null, 'setValue()' => [42], 'prepare()' => []], + ['class' => null, 'setValue()' => [42], 'prepare()' => []], + ], + ]; + } + + /** + * @dataProvider dataInvalidReturnType + */ + public function testUnknownDefinition(mixed $definition, mixed $result): void + { + $exception = new InvalidMiddlewareReturnTypeException($definition, $result); + self::assertSame( + sprintf( + 'Middleware an instance of `Closure` must return an instance of `%s` or `%s`, %s returned.', + MiddlewareInterface::class, + ResponseInterface::class, + ResponseHelper::convertToString($result), + ), + $exception->getMessage() + ); + } + + public static function dataProviderName() + { + yield 'null' => [null]; + yield 'string' => ['test']; + yield 'array' => [[]]; + yield 'object' => [new stdClass()]; + yield 'int' => [42]; + } + + /** + * @dataProvider dataProviderName + */ + public function testName(mixed $result): void + { + $exception = new InvalidMiddlewareReturnTypeException('test', $result); + + self::assertSame( + sprintf('Invalid middleware result type %s', get_debug_type($result)), + $exception->getName() + ); + } +} diff --git a/tests/Helper/ResponseHelperTest.php b/tests/Helper/ResponseHelperTest.php new file mode 100644 index 0000000..daa3776 --- /dev/null +++ b/tests/Helper/ResponseHelperTest.php @@ -0,0 +1,60 @@ + [ + ['error' => true, 'message' => 'Error'], + '"["error" => ..., "message" => ...]" (array of 2 elements)', + ]; + yield 'array 1' => [ + [true], + '"[...]" (array of 1 element)', + ]; + yield 'string' => [ + 'ok', + '"ok" (string)', + ]; + yield 'int' => [ + 555, + '"555" (int)', + ]; + yield 'float' => [ + 555.44, + '"555.44" (float)', + ]; + yield 'double' => [ + 555.444444343434343434343434343434343, + '"555.44444434343" (float)', + ]; + yield 'bool true' => [ + true, + '"true" (bool)', + ]; + yield 'bool false' => [ + false, + '"false" (bool)', + ]; + yield 'stdClass' => [ + new stdClass(), + '"stdClass" (object)', + ]; + } + + /** + * @dataProvider dataResult + */ + public function testResult(mixed $result, string $expected): void + { + $this->assertSame($expected, ResponseHelper::convertToString($result)); + } +} diff --git a/tests/InvalidMiddlewareDefinitionExceptionTest.php b/tests/InvalidMiddlewareDefinitionExceptionTest.php index e6cfa6d..daeccbf 100644 --- a/tests/InvalidMiddlewareDefinitionExceptionTest.php +++ b/tests/InvalidMiddlewareDefinitionExceptionTest.php @@ -26,7 +26,7 @@ public function dataBase(): array ], [ new TestController(), - 'an instance of "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"', + 'an instance of `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController`', 'Related links', ], [ diff --git a/tests/MiddlewareFactoryTest.php b/tests/MiddlewareFactoryTest.php index dd8515a..7949232 100644 --- a/tests/MiddlewareFactoryTest.php +++ b/tests/MiddlewareFactoryTest.php @@ -12,6 +12,7 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Yiisoft\Middleware\Dispatcher\Exception\InvalidMiddlewareReturnTypeException; use Yiisoft\Middleware\Dispatcher\InvalidMiddlewareDefinitionException; use Yiisoft\Middleware\Dispatcher\MiddlewareFactory; use Yiisoft\Middleware\Dispatcher\ParametersResolverInterface; @@ -253,7 +254,12 @@ public function testInvalidMiddlewareWithWrongCallable(): void static fn() => 42 ); - $this->expectException(InvalidMiddlewareDefinitionException::class); + $this->expectException(InvalidMiddlewareReturnTypeException::class); + $this->expectExceptionMessage(sprintf( + 'Middleware an instance of `Closure` must return an instance of `%s` or `%s`, "42" (int) returned.', + MiddlewareInterface::class, + ResponseInterface::class, + )); $middleware->process( $this->createMock(ServerRequestInterface::class), $this->createMock(RequestHandlerInterface::class) @@ -284,7 +290,12 @@ public function testInvalidMiddlewareWithWrongController(): void ->getMiddlewareFactory($container) ->create([InvalidController::class, 'index']); - $this->expectException(InvalidMiddlewareDefinitionException::class); + $this->expectException(InvalidMiddlewareReturnTypeException::class); + $this->expectExceptionMessage(sprintf( + 'Middleware ["Yiisoft\Middleware\Dispatcher\Tests\Support\InvalidController", "index"] must return an instance of `%s` or `%s`, "200" (int) returned.', + MiddlewareInterface::class, + ResponseInterface::class, + )); $middleware->process( $this->createMock(ServerRequestInterface::class), $this->createMock(RequestHandlerInterface::class) From 47876f30e6ca26cfaa256dd723ff3efd7fe00584 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 18 Feb 2024 07:16:31 +0000 Subject: [PATCH 4/9] Apply fixes from StyleCI --- src/Exception/InvalidMiddlewareReturnTypeException.php | 8 -------- src/Helper/DefinitionHelper.php | 4 ++-- src/InvalidMiddlewareDefinitionException.php | 8 -------- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/Exception/InvalidMiddlewareReturnTypeException.php b/src/Exception/InvalidMiddlewareReturnTypeException.php index 6403baa..5d0b330 100644 --- a/src/Exception/InvalidMiddlewareReturnTypeException.php +++ b/src/Exception/InvalidMiddlewareReturnTypeException.php @@ -14,15 +14,7 @@ use Yiisoft\Middleware\Dispatcher\Helper\ResponseHelper; -use function array_slice; -use function count; -use function gettype; use function is_array; -use function is_bool; -use function is_float; -use function is_int; -use function is_object; -use function is_string; final class InvalidMiddlewareReturnTypeException extends InvalidArgumentException implements FriendlyExceptionInterface { diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index 096defd..8de0353 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -21,9 +21,9 @@ final class DefinitionHelper public static function isValidArrayDefinition(mixed $definition): bool { if (!is_array($definition)) { - return false; + return false; } - try{ + try { DefinitionValidator::validateArrayDefinition($definition); return true; } catch (\Exception $e) { diff --git a/src/InvalidMiddlewareDefinitionException.php b/src/InvalidMiddlewareDefinitionException.php index 96807d8..20a09e5 100644 --- a/src/InvalidMiddlewareDefinitionException.php +++ b/src/InvalidMiddlewareDefinitionException.php @@ -11,15 +11,7 @@ use Yiisoft\FriendlyException\FriendlyExceptionInterface; use Yiisoft\Middleware\Dispatcher\Helper\DefinitionHelper; -use function array_slice; -use function count; -use function gettype; use function is_array; -use function is_bool; -use function is_float; -use function is_int; -use function is_object; -use function is_string; final class InvalidMiddlewareDefinitionException extends InvalidArgumentException implements FriendlyExceptionInterface { From b828a3d7864fbecfa7db8b717dc1a57102cc1732 Mon Sep 17 00:00:00 2001 From: xepozz Date: Wed, 21 Feb 2024 20:11:51 +0000 Subject: [PATCH 5/9] Apply Rector changes (CI) --- src/Exception/InvalidMiddlewareReturnTypeException.php | 4 ++-- src/Helper/DefinitionHelper.php | 2 +- src/Helper/ResponseHelper.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Exception/InvalidMiddlewareReturnTypeException.php b/src/Exception/InvalidMiddlewareReturnTypeException.php index 5d0b330..fde3c57 100644 --- a/src/Exception/InvalidMiddlewareReturnTypeException.php +++ b/src/Exception/InvalidMiddlewareReturnTypeException.php @@ -18,11 +18,11 @@ final class InvalidMiddlewareReturnTypeException extends InvalidArgumentException implements FriendlyExceptionInterface { - private string $definitionString; + private readonly string $definitionString; public function __construct( private mixed $definition, - private mixed $result, + private readonly mixed $result, ) { $this->definitionString = DefinitionHelper::convertDefinitionToString($definition); diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index 8de0353..6a7d8ec 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -26,7 +26,7 @@ public static function isValidArrayDefinition(mixed $definition): bool try { DefinitionValidator::validateArrayDefinition($definition); return true; - } catch (\Exception $e) { + } catch (\Exception) { return false; } } diff --git a/src/Helper/ResponseHelper.php b/src/Helper/ResponseHelper.php index 63f066e..9410c6a 100644 --- a/src/Helper/ResponseHelper.php +++ b/src/Helper/ResponseHelper.php @@ -14,7 +14,7 @@ final class ResponseHelper public static function convertToString(mixed $response) { if (is_object($response)) { - return sprintf('"%s" (object)', get_class($response)); + return sprintf('"%s" (object)', $response::class); } if (is_array($response)) { From a67f916ac33a6b4d684dfd0b2a7ddd639c171c19 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Thu, 22 Feb 2024 03:15:11 +0700 Subject: [PATCH 6/9] Fix psalm --- src/Helper/ResponseHelper.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Helper/ResponseHelper.php b/src/Helper/ResponseHelper.php index 63f066e..8a5a0f8 100644 --- a/src/Helper/ResponseHelper.php +++ b/src/Helper/ResponseHelper.php @@ -11,7 +11,7 @@ final class ResponseHelper { - public static function convertToString(mixed $response) + public static function convertToString(mixed $response): string { if (is_object($response)) { return sprintf('"%s" (object)', get_class($response)); @@ -35,9 +35,7 @@ public static function convertToString(mixed $response) $count === 1 ? ' element' : ' elements' ); } - if (is_array($response)) { - return sprintf('"%s" (array)', print_r($response, true)); - } + if (is_bool($response)) { return sprintf('"%s" (bool)', $response ? 'true' : 'false'); } @@ -45,7 +43,7 @@ public static function convertToString(mixed $response) if (is_scalar($response)) { return sprintf( '"%s" (%s)', - $response, + (string)$response, match (true) { is_int($response) => 'int', is_float($response) => 'float', @@ -53,5 +51,7 @@ public static function convertToString(mixed $response) } ); } + + return sprintf('"%s" (%s)', gettype($response), get_debug_type($response)); } } From f17d1ec35093e0fc2cba3a3c04b57bf52772cd7c Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Thu, 22 Feb 2024 03:17:06 +0700 Subject: [PATCH 7/9] Remove redundant code --- src/Helper/DefinitionHelper.php | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index 6a7d8ec..89069a2 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -4,8 +4,6 @@ namespace Yiisoft\Middleware\Dispatcher\Helper; -use Yiisoft\Definitions\Helpers\DefinitionValidator; - use function array_slice; use function count; use function gettype; @@ -18,19 +16,6 @@ final class DefinitionHelper { - public static function isValidArrayDefinition(mixed $definition): bool - { - if (!is_array($definition)) { - return false; - } - try { - DefinitionValidator::validateArrayDefinition($definition); - return true; - } catch (\Exception) { - return false; - } - } - /** * @psalm-assert-if-true string $definition */ From 419631128e20c779aa7ae843bde4b0fc9a02d8ea Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Thu, 22 Feb 2024 03:36:19 +0700 Subject: [PATCH 8/9] Refactor code, add tests --- .../AbstractInvalidMiddlewareException.php | 158 ++++++++++++++++++ .../InvalidMiddlewareReturnTypeException.php | 142 +--------------- src/InvalidMiddlewareDefinitionException.php | 146 ++-------------- ...AbstractInvalidMiddlewareExceptionTest.php | 56 +++++++ ...validMiddlewareDefinitionExceptionTest.php | 71 ++++++++ ...validMiddlewareReturnTypeExceptionTest.php | 70 +------- ...validMiddlewareDefinitionExceptionTest.php | 105 ------------ 7 files changed, 312 insertions(+), 436 deletions(-) create mode 100644 src/Exception/AbstractInvalidMiddlewareException.php create mode 100644 tests/Exception/AbstractInvalidMiddlewareExceptionTest.php create mode 100644 tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php delete mode 100644 tests/InvalidMiddlewareDefinitionExceptionTest.php diff --git a/src/Exception/AbstractInvalidMiddlewareException.php b/src/Exception/AbstractInvalidMiddlewareException.php new file mode 100644 index 0000000..5f1ab4e --- /dev/null +++ b/src/Exception/AbstractInvalidMiddlewareException.php @@ -0,0 +1,158 @@ +definitionString = DefinitionHelper::convertDefinitionToString($definition); + + parent::__construct($message, 0, $previous); + } + + public function getName(): string + { + return 'Invalid middleware definition'; + } + + public function getSolution(): ?string + { + $solution = [ + <<definitionString}` + SOLUTION + ]; + + $suggestion = $this->generateSuggestion(); + if ($suggestion !== null) { + $solution[] = '## Suggestion'; + $solution[] = $suggestion; + } + + $solution[] = << MyMiddleware::class, + '__construct()' => [ + 'someVar' => 42, + ], + ] + ``` + + Closure that returns `ResponseInterface`: + + ```php + static function (): ResponseInterface { + return new Response(418); + }, + ``` + + Closure that returns `MiddlewareInterface`: + + ```php + static function (): MiddlewareInterface { + return new TestMiddleware(); + } + ``` + + Action in controller: + + ```php + [App\Backend\UserController::class, 'index'] + ``` + + ## Related links + + - [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition) + - [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php) + SOLUTION; + + return implode("\n\n", $solution); + } + + private function generateSuggestion(): ?string + { + if (DefinitionHelper::isControllerWithNonExistAction($this->definition)) { + return <<definition[0]}` exists, but does not contain method `{$this->definition[1]}()`. + + Try adding `{$this->definition[1]}()` action to `{$this->definition[0]}` controller: + + ```php + public function {$this->definition[1]}(): ResponseInterface + { + // TODO: Implement your action + } + ``` + SOLUTION; + } + + if (DefinitionHelper::isNotMiddlewareClassName($this->definition)) { + return sprintf( + 'Class `%s` exists, but does not implement `%s`.', + $this->definition, + MiddlewareInterface::class + ); + } + + if (DefinitionHelper::isStringNotClassName($this->definition)) { + return sprintf( + 'Class `%s` not found. It may be needed to install a package with this middleware.', + $this->definition + ); + } + + if (is_array($this->definition)) { + try { + DefinitionValidator::validateArrayDefinition($this->definition); + } catch (InvalidConfigException $e) { + return <<getMessage()} + ``` + SOLUTION; + } + + /** @psalm-suppress MixedArgument In valid array definition element "class" always is string */ + return sprintf( + 'Array definition is valid, class `%s` exists, but does not implement `%s`.', + $this->definition['class'], + MiddlewareInterface::class + ); + } + + return null; + } +} diff --git a/src/Exception/InvalidMiddlewareReturnTypeException.php b/src/Exception/InvalidMiddlewareReturnTypeException.php index fde3c57..30e3c55 100644 --- a/src/Exception/InvalidMiddlewareReturnTypeException.php +++ b/src/Exception/InvalidMiddlewareReturnTypeException.php @@ -4,36 +4,31 @@ namespace Yiisoft\Middleware\Dispatcher\Exception; -use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; -use Yiisoft\Definitions\Exception\InvalidConfigException; -use Yiisoft\Definitions\Helpers\DefinitionValidator; -use Yiisoft\FriendlyException\FriendlyExceptionInterface; +use Throwable; use Yiisoft\Middleware\Dispatcher\Helper\DefinitionHelper; - use Yiisoft\Middleware\Dispatcher\Helper\ResponseHelper; -use function is_array; - -final class InvalidMiddlewareReturnTypeException extends InvalidArgumentException implements FriendlyExceptionInterface +final class InvalidMiddlewareReturnTypeException extends AbstractInvalidMiddlewareException { - private readonly string $definitionString; - public function __construct( - private mixed $definition, + mixed $definition, private readonly mixed $result, + ?Throwable $previous = null, ) { $this->definitionString = DefinitionHelper::convertDefinitionToString($definition); parent::__construct( + $definition, sprintf( 'Middleware %s must return an instance of `%s` or `%s`, %s returned.', $this->definitionString, MiddlewareInterface::class, ResponseInterface::class, ResponseHelper::convertToString($this->result) - ) + ), + $previous, ); } @@ -41,127 +36,4 @@ public function getName(): string { return sprintf('Invalid middleware result type %s', get_debug_type($this->result)); } - - public function getSolution(): ?string - { - $solution = [ - <<definitionString}` - SOLUTION - ]; - - $suggestion = $this->generateSuggestion(); - if ($suggestion !== null) { - $solution[] = '## Suggestion'; - $solution[] = $suggestion; - } - - $solution[] = << MyMiddleware::class, - '__construct()' => [ - 'someVar' => 42, - ], - ] - ``` - - Closure that returns `ResponseInterface`: - - ```php - static function (): ResponseInterface { - return new Response(418); - }, - ``` - - Closure that returns `MiddlewareInterface`: - - ```php - static function (): MiddlewareInterface { - return new TestMiddleware(); - } - ``` - - Action in controller: - - ```php - [App\Backend\UserController::class, 'index'] - ``` - - ## Related links - - - [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition) - - [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php) - SOLUTION; - - return implode("\n\n", $solution); - } - - private function generateSuggestion(): ?string - { - if (DefinitionHelper::isControllerWithNonExistAction($this->definition)) { - return <<definition[0]}` exists, but does not contain method `{$this->definition[1]}()`. - - Try adding `{$this->definition[1]}()` action to `{$this->definition[0]}` controller: - - ```php - public function {$this->definition[1]}(): ResponseInterface - { - // TODO: Implement your action - } - ``` - SOLUTION; - } - - if (DefinitionHelper::isNotMiddlewareClassName($this->definition)) { - return sprintf( - 'Class `%s` exists, but does not implement `%s`.', - $this->definition, - MiddlewareInterface::class - ); - } - - if (DefinitionHelper::isStringNotClassName($this->definition)) { - return sprintf( - 'Class `%s` not found. It may be needed to install a package with this middleware.', - $this->definition - ); - } - - if (is_array($this->definition)) { - try { - DefinitionValidator::validateArrayDefinition($this->definition); - } catch (InvalidConfigException $e) { - return <<getMessage()} - ``` - SOLUTION; - } - - /** @psalm-suppress MixedArgument In valid array definition element "class" always is string */ - return sprintf( - 'Array definition is valid, class `%s` exists, but does not implement `%s`.', - $this->definition['class'], - MiddlewareInterface::class - ); - } - - return null; - } } diff --git a/src/InvalidMiddlewareDefinitionException.php b/src/InvalidMiddlewareDefinitionException.php index 5a513f4..61ffd7b 100644 --- a/src/InvalidMiddlewareDefinitionException.php +++ b/src/InvalidMiddlewareDefinitionException.php @@ -4,26 +4,25 @@ namespace Yiisoft\Middleware\Dispatcher; -use InvalidArgumentException; -use Psr\Http\Server\MiddlewareInterface; -use Yiisoft\Definitions\Exception\InvalidConfigException; -use Yiisoft\Definitions\Helpers\DefinitionValidator; -use Yiisoft\FriendlyException\FriendlyExceptionInterface; +use Throwable; +use Yiisoft\Middleware\Dispatcher\Exception\AbstractInvalidMiddlewareException; use Yiisoft\Middleware\Dispatcher\Helper\DefinitionHelper; -use function is_array; - -final class InvalidMiddlewareDefinitionException extends InvalidArgumentException implements FriendlyExceptionInterface +final class InvalidMiddlewareDefinitionException extends AbstractInvalidMiddlewareException { - private readonly string $definitionString; - public function __construct( - private mixed $definition + mixed $definition, + ?Throwable $previous = null, ) { $this->definitionString = DefinitionHelper::convertDefinitionToString($definition); parent::__construct( - 'Parameter should be either PSR middleware class name or a callable. Got ' . $this->definitionString . '.' + $definition, + sprintf( + 'Parameter should be either PSR middleware class name or a callable. Got %s.', + $this->definitionString + ), + $previous, ); } @@ -31,127 +30,4 @@ public function getName(): string { return 'Invalid middleware definition'; } - - public function getSolution(): ?string - { - $solution = [ - <<definitionString}` - SOLUTION - ]; - - $suggestion = $this->generateSuggestion(); - if ($suggestion !== null) { - $solution[] = '## Suggestion'; - $solution[] = $suggestion; - } - - $solution[] = << MyMiddleware::class, - '__construct()' => [ - 'someVar' => 42, - ], - ] - ``` - - Closure that returns `ResponseInterface`: - - ```php - static function (): ResponseInterface { - return new Response(418); - }, - ``` - - Closure that returns `MiddlewareInterface`: - - ```php - static function (): MiddlewareInterface { - return new TestMiddleware(); - } - ``` - - Action in controller: - - ```php - [App\Backend\UserController::class, 'index'] - ``` - - ## Related links - - - [Array definition syntax](https://github.com/yiisoft/definitions#arraydefinition) - - [Callable PHP documentation](https://www.php.net/manual/language.types.callable.php) - SOLUTION; - - return implode("\n\n", $solution); - } - - private function generateSuggestion(): ?string - { - if (DefinitionHelper::isControllerWithNonExistAction($this->definition)) { - return <<definition[0]}` exists, but does not contain method `{$this->definition[1]}()`. - - Try adding `{$this->definition[1]}()` action to `{$this->definition[0]}` controller: - - ```php - public function {$this->definition[1]}(): ResponseInterface - { - // TODO: Implement your action - } - ``` - SOLUTION; - } - - if (DefinitionHelper::isNotMiddlewareClassName($this->definition)) { - return sprintf( - 'Class `%s` exists, but does not implement `%s`.', - $this->definition, - MiddlewareInterface::class - ); - } - - if (DefinitionHelper::isStringNotClassName($this->definition)) { - return sprintf( - 'Class `%s` not found. It may be needed to install a package with this middleware.', - $this->definition - ); - } - - if (is_array($this->definition)) { - try { - DefinitionValidator::validateArrayDefinition($this->definition); - } catch (InvalidConfigException $e) { - return <<getMessage()} - ``` - SOLUTION; - } - - /** @psalm-suppress MixedArgument In valid array definition element "class" always is string */ - return sprintf( - 'Array definition is valid, class `%s` exists, but does not implement `%s`.', - $this->definition['class'], - MiddlewareInterface::class - ); - } - - return null; - } } diff --git a/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php b/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php new file mode 100644 index 0000000..ee2f6d6 --- /dev/null +++ b/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php @@ -0,0 +1,56 @@ + TestController::class, 'index'], + 'You may have an error in array definition. Array definition validation result', + ], + [ + ['class' => TestController::class], + 'Array definition is valid, ' . + 'class `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` exists, ' . + 'but does not implement `Psr\Http\Server\MiddlewareInterface`.', + ], + ]; + } + + /** + * @dataProvider dataSolution + */ + public function testSolution(mixed $definition, string $expectedSolution): void + { + $exception = static::createException($definition); + self::assertStringContainsString($expectedSolution, $exception->getSolution()); + } + + abstract protected function createException(mixed $definition): Throwable; +} diff --git a/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php b/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php new file mode 100644 index 0000000..b8bd0dc --- /dev/null +++ b/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php @@ -0,0 +1,71 @@ + TestController::class, 'index'], + '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', + ], + [ + ['object' => TestController::class, 'index'], + '["object" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', + ], + [ + ['class' => TestController::class], + '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"]', + ], + ]; + } + + /** + * @dataProvider dataExceptionMessage + */ + public function testExceptionMessage(mixed $definition, string $expectedMessage): void + { + $exception = self::createException($definition); + self::assertStringEndsWith('. Got ' . $expectedMessage . '.', $exception->getMessage()); + } + + public function testName(): void + { + $exception = new InvalidMiddlewareDefinitionException('test'); + + self::assertSame( + 'Invalid middleware definition', + $exception->getName() + ); + } + + protected function createException(mixed $definition): Throwable + { + return new InvalidMiddlewareDefinitionException($definition); + } +} diff --git a/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php index 37c29b6..89e9821 100644 --- a/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php +++ b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php @@ -4,73 +4,16 @@ namespace Yiisoft\Middleware\Dispatcher\Tests\Exception; -use PHPUnit\Framework\TestCase; +use Exception\AbstractInvalidMiddlewareExceptionTest; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use stdClass; +use Throwable; use Yiisoft\Middleware\Dispatcher\Exception\InvalidMiddlewareReturnTypeException; use Yiisoft\Middleware\Dispatcher\Helper\ResponseHelper; -use Yiisoft\Middleware\Dispatcher\InvalidMiddlewareDefinitionException; -use Yiisoft\Middleware\Dispatcher\Tests\Support\TestController; -final class InvalidMiddlewareReturnTypeExceptionTest extends TestCase +final class InvalidMiddlewareReturnTypeExceptionTest extends AbstractInvalidMiddlewareExceptionTest { - public function dataBase(): array - { - return [ - [ - 'test', - '"test"', - 'Class `test` not found', - ], - [ - TestController::class, - '"Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"', - 'Class `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` exists, but does not implement', - ], - [ - new TestController(), - 'an instance of `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController`', - 'Related links', - ], - [ - [TestController::class, 'notExistsAction'], - '["Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "notExistsAction"]', - 'Try adding `notExistsAction()` action to ' . - '`Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` controller:', - ], - [ - ['class' => TestController::class, 'index'], - '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', - null, - ], - [ - ['object' => TestController::class, 'index'], - '["object" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', - 'You may have an error in array definition. Array definition validation result', - ], - [ - ['class' => TestController::class], - '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"]', - 'Array definition is valid, ' . - 'class `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` exists, ' . - 'but does not implement `Psr\Http\Server\MiddlewareInterface`.', - ], - ]; - } - - /** - * @dataProvider dataBase - */ - public function testBase(mixed $definition, string $expectedMessage, ?string $expectedSolution): void - { - $exception = new InvalidMiddlewareDefinitionException($definition); - self::assertStringEndsWith('. Got ' . $expectedMessage . '.', $exception->getMessage()); - if ($expectedSolution !== null) { - self::assertStringContainsString($expectedSolution, $exception->getSolution()); - } - } - public function dataInvalidReturnType(): array { return [ @@ -102,7 +45,7 @@ public function testUnknownDefinition(mixed $definition, mixed $result): void ); } - public static function dataProviderName() + public static function dataProviderName(): iterable { yield 'null' => [null]; yield 'string' => ['test']; @@ -123,4 +66,9 @@ public function testName(mixed $result): void $exception->getName() ); } + + protected function createException(mixed $definition): Throwable + { + return new InvalidMiddlewareReturnTypeException($definition, new stdClass()); + } } diff --git a/tests/InvalidMiddlewareDefinitionExceptionTest.php b/tests/InvalidMiddlewareDefinitionExceptionTest.php deleted file mode 100644 index daeccbf..0000000 --- a/tests/InvalidMiddlewareDefinitionExceptionTest.php +++ /dev/null @@ -1,105 +0,0 @@ - TestController::class, 'index'], - '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', - null, - ], - [ - ['object' => TestController::class, 'index'], - '["object" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController", "index"]', - 'You may have an error in array definition. Array definition validation result', - ], - [ - ['class' => TestController::class], - '["class" => "Yiisoft\Middleware\Dispatcher\Tests\Support\TestController"]', - 'Array definition is valid, ' . - 'class `Yiisoft\Middleware\Dispatcher\Tests\Support\TestController` exists, ' . - 'but does not implement `Psr\Http\Server\MiddlewareInterface`.', - ], - ]; - } - - /** - * @dataProvider dataBase - */ - public function testBase(mixed $definition, string $expectedMessage, ?string $expectedSolution): void - { - $exception = new InvalidMiddlewareDefinitionException($definition); - self::assertStringEndsWith('. Got ' . $expectedMessage . '.', $exception->getMessage()); - if ($expectedSolution !== null) { - self::assertStringContainsString($expectedSolution, $exception->getSolution()); - } - } - - public function dataUnknownDefinition(): array - { - return [ - [42, '42'], - [[new stdClass()], '[stdClass]'], - [true, 'true'], - [false, 'false'], - [ - ['class' => null, 'setValue()' => [42], 'prepare()' => []], - '["class" => null, "setValue()" => array, ...]', - ], - ]; - } - - /** - * @dataProvider dataUnknownDefinition - */ - public function testUnknownDefinition(mixed $definition, string $value): void - { - $exception = new InvalidMiddlewareDefinitionException($definition); - self::assertSame( - 'Parameter should be either PSR middleware class name or a callable. Got ' . $value . '.', - $exception->getMessage() - ); - } - - public function testName(): void - { - $exception = new InvalidMiddlewareDefinitionException('test'); - - self::assertSame( - 'Invalid middleware definition', - $exception->getName() - ); - } -} From c26b39215c0d3ccee886a5c28912c30ea9dbb315 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Thu, 22 Feb 2024 03:57:47 +0700 Subject: [PATCH 9/9] 100% CC --- .../AbstractInvalidMiddlewareException.php | 5 -- .../InvalidMiddlewareReturnTypeException.php | 2 +- src/Helper/DefinitionHelper.php | 6 +- src/InvalidMiddlewareDefinitionException.php | 2 +- ...AbstractInvalidMiddlewareExceptionTest.php | 2 +- ...validMiddlewareDefinitionExceptionTest.php | 1 - ...validMiddlewareReturnTypeExceptionTest.php | 72 ++++++++++++++++--- 7 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/Exception/AbstractInvalidMiddlewareException.php b/src/Exception/AbstractInvalidMiddlewareException.php index 5f1ab4e..f1b1e3b 100644 --- a/src/Exception/AbstractInvalidMiddlewareException.php +++ b/src/Exception/AbstractInvalidMiddlewareException.php @@ -28,11 +28,6 @@ public function __construct( parent::__construct($message, 0, $previous); } - public function getName(): string - { - return 'Invalid middleware definition'; - } - public function getSolution(): ?string { $solution = [ diff --git a/src/Exception/InvalidMiddlewareReturnTypeException.php b/src/Exception/InvalidMiddlewareReturnTypeException.php index 30e3c55..c4a463b 100644 --- a/src/Exception/InvalidMiddlewareReturnTypeException.php +++ b/src/Exception/InvalidMiddlewareReturnTypeException.php @@ -26,7 +26,7 @@ public function __construct( $this->definitionString, MiddlewareInterface::class, ResponseInterface::class, - ResponseHelper::convertToString($this->result) + ResponseHelper::convertToString($this->result), ), $previous, ); diff --git a/src/Helper/DefinitionHelper.php b/src/Helper/DefinitionHelper.php index 89069a2..ed82ebb 100644 --- a/src/Helper/DefinitionHelper.php +++ b/src/Helper/DefinitionHelper.php @@ -82,13 +82,13 @@ private static function convertToString(mixed $value): string } if ($value === null) { - return 'null'; + return '"null"'; } if (is_object($value)) { - return $value::class; + return sprintf('"%s"', $value::class); } - return gettype($value); + return sprintf('"%s"', gettype($value)); } } diff --git a/src/InvalidMiddlewareDefinitionException.php b/src/InvalidMiddlewareDefinitionException.php index 61ffd7b..5cca7fd 100644 --- a/src/InvalidMiddlewareDefinitionException.php +++ b/src/InvalidMiddlewareDefinitionException.php @@ -20,7 +20,7 @@ public function __construct( $definition, sprintf( 'Parameter should be either PSR middleware class name or a callable. Got %s.', - $this->definitionString + $this->definitionString, ), $previous, ); diff --git a/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php b/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php index ee2f6d6..490dda2 100644 --- a/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php +++ b/tests/Exception/AbstractInvalidMiddlewareExceptionTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Exception; +namespace Yiisoft\Middleware\Dispatcher\Tests\Exception; use PHPUnit\Framework\TestCase; use Throwable; diff --git a/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php b/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php index b8bd0dc..d4c642a 100644 --- a/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php +++ b/tests/Exception/InvalidMiddlewareDefinitionExceptionTest.php @@ -4,7 +4,6 @@ namespace Yiisoft\Middleware\Dispatcher\Tests\Exception; -use Exception\AbstractInvalidMiddlewareExceptionTest; use Throwable; use Yiisoft\Middleware\Dispatcher\InvalidMiddlewareDefinitionException; use Yiisoft\Middleware\Dispatcher\Tests\Support\TestController; diff --git a/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php index 89e9821..2bfce43 100644 --- a/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php +++ b/tests/Exception/InvalidMiddlewareReturnTypeExceptionTest.php @@ -4,24 +4,74 @@ namespace Yiisoft\Middleware\Dispatcher\Tests\Exception; -use Exception\AbstractInvalidMiddlewareExceptionTest; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\MiddlewareInterface; use stdClass; use Throwable; use Yiisoft\Middleware\Dispatcher\Exception\InvalidMiddlewareReturnTypeException; -use Yiisoft\Middleware\Dispatcher\Helper\ResponseHelper; final class InvalidMiddlewareReturnTypeExceptionTest extends AbstractInvalidMiddlewareExceptionTest { public function dataInvalidReturnType(): array { return [ - [fn() => 42, 42], - [fn() => [new stdClass()], new stdClass()], - [fn() => true, true], - [fn() => false, false], [ + 'Middleware "null"', + null, + 'null', + ], + [ + 'Middleware ["key1" => "null"]', + ['key1' => null], + 'null', + ], + [ + 'Middleware ["key1" => "stdClass"]', + ['key1' => new stdClass()], + 'null', + ], + [ + 'Middleware ["key1" => true, "key2" => false]', + ['key1' => true, 'key2' => false], + 'null', + ], + [ + 'Middleware ["key1" => "null", "key2" => "null", ...]', + ['key1' => null, 'key2' => null, 'key3' => null], + 'null', + ], + [ + 'Middleware ["key" => "null"]', + ['key' => null], + 'null', + ], + [ + 'Middleware "resource"', + fopen('php://memory', 'r'), + 'null', + ], + [ + 'Middleware an instance of `Closure`', + fn() => 42, + 42, + ], + [ + 'Middleware an instance of `Closure`', + fn() => [new stdClass()], + new stdClass(), + ], + [ + 'Middleware an instance of `Closure`', + fn() => true, + true, + ], + [ + 'Middleware an instance of `Closure`', + fn() => false, + false, + ], + [ + 'Middleware an instance of `Closure`', fn() => ['class' => null, 'setValue()' => [42], 'prepare()' => []], ['class' => null, 'setValue()' => [42], 'prepare()' => []], ], @@ -31,15 +81,15 @@ public function dataInvalidReturnType(): array /** * @dataProvider dataInvalidReturnType */ - public function testUnknownDefinition(mixed $definition, mixed $result): void + public function testUnknownDefinition(string $startMessage, mixed $definition, mixed $result): void { - $exception = new InvalidMiddlewareReturnTypeException($definition, $result); - self::assertSame( + $exception = new InvalidMiddlewareReturnTypeException($definition, null); + self::assertStringStartsWith( sprintf( - 'Middleware an instance of `Closure` must return an instance of `%s` or `%s`, %s returned.', + '%s must return an instance of `%s` or `%s`', + $startMessage, MiddlewareInterface::class, ResponseInterface::class, - ResponseHelper::convertToString($result), ), $exception->getMessage() );