From 219f9d95818dc8f5a28b966341651f2f5befc349 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Thu, 28 Dec 2023 13:37:36 +0200 Subject: [PATCH 1/4] Add non-reportable exceptions --- src/Exceptions/src/ExceptionHandler.php | 31 +++++++++++ src/Exceptions/tests/ExceptionHandlerTest.php | 54 +++++++++++++++++++ .../ClientException/ServerErrorException.php | 1 + src/Http/tests/ExceptionsTest.php | 8 --- 4 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/Exceptions/src/ExceptionHandler.php b/src/Exceptions/src/ExceptionHandler.php index dda0c6ba3..c43a93d84 100644 --- a/src/Exceptions/src/ExceptionHandler.php +++ b/src/Exceptions/src/ExceptionHandler.php @@ -6,6 +6,9 @@ use Closure; use Spiral\Exceptions\Renderer\PlainRenderer; +use Spiral\Filters\Exception\AuthorizationException; +use Spiral\Filters\Exception\ValidationException; +use Spiral\Http\Exception\ClientException; /** * The class is responsible for: @@ -24,6 +27,11 @@ class ExceptionHandler implements ExceptionHandlerInterface /** @var array */ protected array $reporters = []; protected mixed $output = null; + protected array $nonReportableExceptions = [ + ClientException::class, + AuthorizationException::class, + ValidationException::class, + ]; public function __construct() { @@ -64,6 +72,10 @@ public function canRender(string $format): bool public function report(\Throwable $exception): void { + if ($this->shouldNotReport($exception)) { + return; + } + foreach ($this->reporters as $reporter) { try { if ($reporter instanceof ExceptionReporterInterface) { @@ -106,6 +118,14 @@ public function addRenderer(ExceptionRendererInterface $renderer): void \array_unshift($this->renderers, $renderer); } + /** + * @param class-string<\Throwable> $exception + */ + public function dontReport(string $exception): void + { + $this->nonReportableExceptions[] = $exception; + } + /** * @param ExceptionReporterInterface|Closure(\Throwable):void $reporter */ @@ -160,4 +180,15 @@ protected function bootBasicHandlers(): void { $this->addRenderer(new PlainRenderer()); } + + protected function shouldNotReport(\Throwable $exception): bool + { + foreach ($this->nonReportableExceptions as $nonReportableException) { + if ($exception instanceof $nonReportableException) { + return true; + } + } + + return false; + } } diff --git a/src/Exceptions/tests/ExceptionHandlerTest.php b/src/Exceptions/tests/ExceptionHandlerTest.php index d5b63885f..6128c1467 100644 --- a/src/Exceptions/tests/ExceptionHandlerTest.php +++ b/src/Exceptions/tests/ExceptionHandlerTest.php @@ -5,11 +5,18 @@ namespace Spiral\Tests\Exceptions; use Mockery as m; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use RuntimeException; use Spiral\Exceptions\ExceptionHandler; use Spiral\Exceptions\ExceptionRendererInterface; use Spiral\Exceptions\ExceptionReporterInterface; +use Spiral\Filters\Exception\AuthorizationException; +use Spiral\Filters\Exception\ValidationException; +use Spiral\Http\Exception\ClientException; +use Spiral\Http\Exception\ClientException\ForbiddenException; +use Spiral\Http\Exception\ClientException\NotFoundException; +use Spiral\Http\Exception\ClientException\UnauthorizedException; class ExceptionHandlerTest extends TestCase { @@ -91,6 +98,38 @@ public function testAllReportersShouldBeCalled(): void $this->assertTrue(true); } + #[DataProvider('nonReportableExceptionsDataProvider')] + public function testNonReportableExceptions(\Throwable $exception): void + { + $reporter = $this->createMock(ExceptionReporterInterface::class); + $reporter->expects($this->never())->method('report'); + + $handler = $this->makeEmptyErrorHandler(); + $handler->addReporter($reporter); + + $handler->report($exception); + } + + public function testAddNonReportableExceptions(): void + { + $handler = $this->makeEmptyErrorHandler(); + $ref = new \ReflectionProperty($handler, 'nonReportableExceptions'); + $this->assertSame([ + ClientException::class, + AuthorizationException::class, + ValidationException::class, + ], $ref->getValue($handler)); + + $handler->dontReport(\DomainException::class); + + $this->assertSame([ + ClientException::class, + AuthorizationException::class, + ValidationException::class, + \DomainException::class + ], $ref->getValue($handler)); + } + private function makeEmptyErrorHandler(): ExceptionHandler { return new class extends ExceptionHandler { @@ -104,4 +143,19 @@ private function makeErrorHandler(): ExceptionHandler { return new ExceptionHandler(); } + + public static function nonReportableExceptionsDataProvider(): \Traversable + { + yield [new class extends ClientException {}]; + yield [new NotFoundException()]; + yield [new class extends NotFoundException {}]; + yield [new ForbiddenException()]; + yield [new class extends ForbiddenException {}]; + yield [new UnauthorizedException()]; + yield [new class extends UnauthorizedException {}]; + yield [new AuthorizationException()]; + yield [new class extends AuthorizationException {}]; + yield [new ValidationException([])]; + yield [new class([]) extends ValidationException {}]; + } } diff --git a/src/Http/src/Exception/ClientException/ServerErrorException.php b/src/Http/src/Exception/ClientException/ServerErrorException.php index 8afd24003..ede31697b 100644 --- a/src/Http/src/Exception/ClientException/ServerErrorException.php +++ b/src/Http/src/Exception/ClientException/ServerErrorException.php @@ -8,6 +8,7 @@ /** * HTTP 500 exception. + * @deprecated since v3.11.1. */ class ServerErrorException extends ClientException { diff --git a/src/Http/tests/ExceptionsTest.php b/src/Http/tests/ExceptionsTest.php index 895995f36..60ce2b298 100644 --- a/src/Http/tests/ExceptionsTest.php +++ b/src/Http/tests/ExceptionsTest.php @@ -11,7 +11,6 @@ use Spiral\Http\Exception\ClientException\BadRequestException; use Spiral\Http\Exception\ClientException\ForbiddenException; use Spiral\Http\Exception\ClientException\NotFoundException; -use Spiral\Http\Exception\ClientException\ServerErrorException; use Spiral\Http\Exception\ClientException\UnauthorizedException; class ExceptionsTest extends TestCase @@ -46,12 +45,6 @@ public function testUnauthorized(): void $this->assertSame(401, $e->getCode()); } - public function testServerError(): void - { - $e = new ServerErrorException(); - $this->assertSame(500, $e->getCode()); - } - #[DataProvider('allExceptionsWithPreviousSet')] public function testPreviousSetter(\Throwable $exception): void { @@ -63,7 +56,6 @@ public static function allExceptionsWithPreviousSet(): \Generator yield [new Exception\ClientException\BadRequestException('', new \Exception())]; yield [new Exception\ClientException\ForbiddenException('', new \Exception())]; yield [new Exception\ClientException\NotFoundException('', new \Exception())]; - yield [new Exception\ClientException\ServerErrorException('', new \Exception())]; yield [new Exception\ClientException\UnauthorizedException('', new \Exception())]; yield [new Exception\ClientException(0, '', new \Exception())]; yield [new Exception\DotNotFoundException('', 0, new \Exception())]; From c3efa7008bd76790b35ad6f4cd4111b08d0e839e Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Thu, 28 Dec 2023 13:49:11 +0200 Subject: [PATCH 2/4] Fix phpunit/phpunit version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 73a384e93..97d37e2f6 100644 --- a/composer.json +++ b/composer.json @@ -130,7 +130,7 @@ "league/flysystem-aws-s3-v3": "^2.0", "mikey179/vfsstream": "^1.6", "mockery/mockery": "^1.5", - "phpunit/phpunit": "^10.1", + "phpunit/phpunit": "10.5.3", "ramsey/collection": "^1.2", "ramsey/uuid": "^4.2.3", "rector/rector": "0.18.1", From 00f054a9ecba19ea866fe65e3bc3b4db3bcbeb70 Mon Sep 17 00:00:00 2001 From: Maxim Smakouz Date: Tue, 2 Jan 2024 11:10:37 +0200 Subject: [PATCH 3/4] Add NonReportable attribute --- src/Exceptions/src/Attribute/NonReportable.php | 10 ++++++++++ src/Exceptions/src/ExceptionHandler.php | 5 ++++- src/Exceptions/tests/ExceptionHandlerTest.php | 2 ++ src/Exceptions/tests/Fixtures/TestException.php | 12 ++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/Exceptions/src/Attribute/NonReportable.php create mode 100644 src/Exceptions/tests/Fixtures/TestException.php diff --git a/src/Exceptions/src/Attribute/NonReportable.php b/src/Exceptions/src/Attribute/NonReportable.php new file mode 100644 index 000000000..9514189f6 --- /dev/null +++ b/src/Exceptions/src/Attribute/NonReportable.php @@ -0,0 +1,10 @@ +getAttributes(NonReportable::class)[0] ?? null; + + return $attribute !== null; } } diff --git a/src/Exceptions/tests/ExceptionHandlerTest.php b/src/Exceptions/tests/ExceptionHandlerTest.php index 6128c1467..23236b5de 100644 --- a/src/Exceptions/tests/ExceptionHandlerTest.php +++ b/src/Exceptions/tests/ExceptionHandlerTest.php @@ -17,6 +17,7 @@ use Spiral\Http\Exception\ClientException\ForbiddenException; use Spiral\Http\Exception\ClientException\NotFoundException; use Spiral\Http\Exception\ClientException\UnauthorizedException; +use Spiral\Tests\Exceptions\Fixtures\TestException; class ExceptionHandlerTest extends TestCase { @@ -157,5 +158,6 @@ public static function nonReportableExceptionsDataProvider(): \Traversable yield [new class extends AuthorizationException {}]; yield [new ValidationException([])]; yield [new class([]) extends ValidationException {}]; + yield [new TestException()]; } } diff --git a/src/Exceptions/tests/Fixtures/TestException.php b/src/Exceptions/tests/Fixtures/TestException.php new file mode 100644 index 000000000..9135db853 --- /dev/null +++ b/src/Exceptions/tests/Fixtures/TestException.php @@ -0,0 +1,12 @@ + Date: Tue, 2 Jan 2024 12:16:19 +0200 Subject: [PATCH 4/4] Fix deprecation message --- src/Http/src/Exception/ClientException/ServerErrorException.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/src/Exception/ClientException/ServerErrorException.php b/src/Http/src/Exception/ClientException/ServerErrorException.php index ede31697b..1dc0a6c0c 100644 --- a/src/Http/src/Exception/ClientException/ServerErrorException.php +++ b/src/Http/src/Exception/ClientException/ServerErrorException.php @@ -8,7 +8,7 @@ /** * HTTP 500 exception. - * @deprecated since v3.11.1. + * @deprecated since v3.12 */ class ServerErrorException extends ClientException {