From b2df1f6cece5d2bad602e0974955697412632c99 Mon Sep 17 00:00:00 2001 From: Alexey Deriyenko Date: Sun, 5 Dec 2021 16:35:26 +0100 Subject: [PATCH 1/2] squashing --- .../FrameworkExtension.php | 3 + .../Resources/config/error_renderer.php | 1 + .../Resources/config/serializer.php | 1 + .../FrameworkBundle/Resources/config/web.php | 2 +- .../ErrorRenderer/HtmlErrorRenderer.php | 20 +++-- .../ErrorRenderer/SerializerErrorRenderer.php | 21 ++++-- .../ErrorRenderer/HtmlErrorRendererTest.php | 39 ++++++++++ .../SerializerErrorRendererTest.php | 73 +++++++++++++++++++ 8 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 src/Symfony/Component/Serializer/Tests/ErrorRenderer/SerializerErrorRendererTest.php diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index 22517ad5c7d3f..9d80f497b2c89 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -452,6 +452,7 @@ public function load(array $configs, ContainerBuilder $container) $this->registerSecretsConfiguration($config['secrets'], $container, $loader); $container->getDefinition('exception_listener')->replaceArgument(3, $config['exceptions']); + $container->getDefinition('error_handler.error_renderer.html')->replaceArgument(6, $config['exceptions']); if ($this->isConfigEnabled($container, $config['serializer'])) { if (!class_exists(\Symfony\Component\Serializer\Serializer::class)) { @@ -459,6 +460,8 @@ public function load(array $configs, ContainerBuilder $container) } $this->registerSerializerConfiguration($config['serializer'], $container, $loader); + + $container->getDefinition('error_handler.error_renderer.serializer')->replaceArgument(4, $config['exceptions']); } if ($propertyInfoEnabled) { diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php index 67f28ce44d838..abc0c67e60cff 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php @@ -30,6 +30,7 @@ ->factory([HtmlErrorRenderer::class, 'getAndCleanOutputBuffer']) ->args([service('request_stack')]), service('logger')->nullOnInvalid(), + abstract_arg('Configuration per exception class'), ]) ->alias('error_renderer.html', 'error_handler.error_renderer.html') diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php index dfb2589cba315..0176f8677b75f 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php @@ -208,6 +208,7 @@ inline_service() ->factory([HtmlErrorRenderer::class, 'isDebug']) ->args([service('request_stack'), param('kernel.debug')]), + abstract_arg('Configuration per exception class'), ]) ; diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php index 00b8d8aafbd5a..0b96a819e3e51 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php @@ -106,7 +106,7 @@ param('kernel.error_controller'), service('logger')->nullOnInvalid(), param('kernel.debug'), - abstract_arg('an exceptions to log & status code mapping'), + abstract_arg('Configuration per exception class'), ]) ->tag('kernel.event_subscriber') ->tag('monolog.logger', ['channel' => 'request']) diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php index 6f12185a068ca..a12149f369579 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php @@ -39,15 +39,17 @@ class HtmlErrorRenderer implements ErrorRendererInterface private $projectDir; private $outputBuffer; private $logger; + private $exceptionsMapping; private static $template = 'views/error.html.php'; /** - * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it + * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it * @param string|FileLinkFormatter|null $fileLinkFormat - * @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it + * @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it + * @param array $exceptionsMapping Configuration per exception class */ - public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null) + public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null, array $exceptionsMapping = []) { if (!\is_bool($debug) && !\is_callable($debug)) { throw new \TypeError(sprintf('Argument 1 passed to "%s()" must be a boolean or a callable, "%s" given.', __METHOD__, \gettype($debug))); @@ -63,6 +65,7 @@ public function __construct($debug = false, string $charset = null, $fileLinkFor $this->projectDir = $projectDir; $this->outputBuffer = $outputBuffer; $this->logger = $logger; + $this->exceptionsMapping = $exceptionsMapping; } /** @@ -76,7 +79,14 @@ public function render(\Throwable $exception): FlattenException $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } - $exception = FlattenException::createFromThrowable($exception, null, $headers); + $statusCode = 500; + foreach ($this->exceptionsMapping as $class => $config) { + if ($exception instanceof $class && $config['status_code']) { + $statusCode = $config['status_code']; + break; + } + } + $exception = FlattenException::createFromThrowable($exception, $statusCode, $headers); return $exception->setAsString($this->renderException($exception)); } @@ -262,8 +272,6 @@ private function formatFile(string $file, int $line, string $text = null): strin * @param string $file A file path * @param int $line The selected line number * @param int $srcContext The number of displayed lines around or -1 for the whole file - * - * @return string */ private function fileExcerpt(string $file, int $line, int $srcContext = 3): string { diff --git a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php index cec8e4d413dce..f49bd3aec94cd 100644 --- a/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php +++ b/src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php @@ -28,13 +28,15 @@ class SerializerErrorRenderer implements ErrorRendererInterface private $format; private $fallbackErrorRenderer; private $debug; + private $exceptionsMapping; /** - * @param string|callable(FlattenException) $format The format as a string or a callable that should return it - * formats not supported by Request::getMimeTypes() should be given as mime types - * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it + * @param string|callable(FlattenException) $format The format as a string or a callable that should return it + * formats not supported by Request::getMimeTypes() should be given as mime types + * @param bool|callable $debug The debugging mode as a boolean or a callable that should return it + * @param array $exceptionsMapping Configuration per exception class */ - public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false) + public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false, array $exceptionsMapping = []) { if (!\is_string($format) && !\is_callable($format)) { throw new \TypeError(sprintf('Argument 2 passed to "%s()" must be a string or a callable, "%s" given.', __METHOD__, \gettype($format))); @@ -48,6 +50,7 @@ public function __construct(SerializerInterface $serializer, $format, ErrorRende $this->format = $format; $this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer(); $this->debug = $debug; + $this->exceptionsMapping = $exceptionsMapping; } /** @@ -62,7 +65,15 @@ public function render(\Throwable $exception): FlattenException $headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine(); } - $flattenException = FlattenException::createFromThrowable($exception, null, $headers); + $statusCode = 500; + foreach ($this->exceptionsMapping as $class => $config) { + if ($exception instanceof $class && $config['status_code']) { + $statusCode = $config['status_code']; + break; + } + } + + $flattenException = FlattenException::createFromThrowable($exception, $statusCode, $headers); try { $format = \is_string($this->format) ? $this->format : ($this->format)($flattenException); diff --git a/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php index f292d0f79618f..34ed98faa47b6 100644 --- a/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php +++ b/src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer; +use Symfony\Component\HttpFoundation\Response; class HtmlErrorRendererTest extends TestCase { @@ -40,6 +41,22 @@ public function getRenderData(): iterable %AAn Error Occurred: Internal Server Error %A

The server returned a "500 Internal Server Error".

%A +HTML; + + $expectedDebugWithStatusCode = << + + +%AFoo (418 I'm a teapot) +%A
%A + +HTML; + + $expectedNonDebugWithStatusCode = << + +%AAn Error Occurred: I'm a teapot +%A

The server returned a "418 I'm a teapot".

%A HTML; yield '->render() returns the HTML content WITH stack traces in debug mode' => [ @@ -53,5 +70,27 @@ public function getRenderData(): iterable new HtmlErrorRenderer(false), $expectedNonDebug, ]; + + yield '->render() returns the HTML content WITH stack traces in debug mode and contains the correct status code' => [ + new \RuntimeException('Foo'), + new HtmlErrorRenderer(true, null, null, null, '', null, [ + \RuntimeException::class => [ + 'status_code' => Response::HTTP_I_AM_A_TEAPOT, + 'log_level' => null, + ], + ]), + $expectedDebugWithStatusCode, + ]; + + yield '->render() returns the HTML content WITHOUT stack traces in non-debug mode and contains the correct status code' => [ + new \RuntimeException('Foo'), + new HtmlErrorRenderer(false, null, null, null, '', null, [ + \RuntimeException::class => [ + 'status_code' => Response::HTTP_I_AM_A_TEAPOT, + 'log_level' => null, + ], + ]), + $expectedNonDebugWithStatusCode, + ]; } } diff --git a/src/Symfony/Component/Serializer/Tests/ErrorRenderer/SerializerErrorRendererTest.php b/src/Symfony/Component/Serializer/Tests/ErrorRenderer/SerializerErrorRendererTest.php new file mode 100644 index 0000000000000..2be2692b74674 --- /dev/null +++ b/src/Symfony/Component/Serializer/Tests/ErrorRenderer/SerializerErrorRendererTest.php @@ -0,0 +1,73 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Serializer\Tests\ErrorRenderer; + +use function json_decode; +use PHPUnit\Framework\TestCase; +use Symfony\Component\ErrorHandler\ErrorRenderer\SerializerErrorRenderer; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Serializer\Encoder\JsonEncoder; +use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; +use Symfony\Component\Serializer\Serializer; + +/** + * @author Alexey Deriyenko + */ +class SerializerErrorRendererTest extends TestCase +{ + /** + * @dataProvider getRenderData + */ + public function testRenderReturnsJson(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer) + { + $this->assertJson($serializerErrorRenderer->render($exception)->getAsString()); + } + + /** + * @dataProvider getRenderData + */ + public function testRenderReturnsJsonWithCorrectStatusCode(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer, int $expectedStatusCode) + { + $statusCodeFromJson = json_decode($serializerErrorRenderer->render($exception)->getAsString())->statusCode; + $this->assertEquals($expectedStatusCode, $statusCodeFromJson); + } + + /** + * @dataProvider getRenderData + */ + public function testRenderReturnsJsonWithCorrectStatusText(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer, int $expectedStatusCode, string $expectedStatusText) + { + $statusTextFromJson = json_decode($serializerErrorRenderer->render($exception)->getAsString())->statusText; + $this->assertEquals($expectedStatusText, $statusTextFromJson); + } + + public function getRenderData(): iterable + { + yield '->render() returns the JSON content without exception mapping config' => [ + new \RuntimeException('Foo'), + new SerializerErrorRenderer(new Serializer([new ObjectNormalizer()], [new JsonEncoder()]), 'json'), + Response::HTTP_INTERNAL_SERVER_ERROR, + Response::$statusTexts[Response::HTTP_INTERNAL_SERVER_ERROR], + ]; + + yield '->render() returns the JSON content with exception mapping config' => [ + new \RuntimeException('Foo'), + new SerializerErrorRenderer(new Serializer([new ObjectNormalizer()], [new JsonEncoder()]), 'json', null, false, [ + \RuntimeException::class => [ + 'status_code' => Response::HTTP_I_AM_A_TEAPOT, + 'log_level' => null, + ], + ]), + Response::HTTP_I_AM_A_TEAPOT, + Response::$statusTexts[Response::HTTP_I_AM_A_TEAPOT], + ]; + } +} From e215a267d0dcd4a0659952bc1008820581475745 Mon Sep 17 00:00:00 2001 From: Alexey Deriyenko Date: Fri, 10 Dec 2021 22:23:03 +0100 Subject: [PATCH 2/2] bumping error handler version --- src/Symfony/Bundle/FrameworkBundle/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/composer.json b/src/Symfony/Bundle/FrameworkBundle/composer.json index 71e4940ff0a4b..eb57788da90cc 100644 --- a/src/Symfony/Bundle/FrameworkBundle/composer.json +++ b/src/Symfony/Bundle/FrameworkBundle/composer.json @@ -23,7 +23,7 @@ "symfony/dependency-injection": "^5.3|^6.0", "symfony/deprecation-contracts": "^2.1|^3", "symfony/event-dispatcher": "^5.1|^6.0", - "symfony/error-handler": "^4.4.1|^5.0.1|^6.0", + "symfony/error-handler": "^5.4.2|^6.0.2", "symfony/http-foundation": "^5.3|^6.0", "symfony/http-kernel": "^5.4|^6.0", "symfony/polyfill-mbstring": "~1.0", pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy