Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit b2df1f6

Browse filesBrowse files
author
Alexey Deriyenko
committed
squashing
1 parent 4e7e429 commit b2df1f6
Copy full SHA for b2df1f6

File tree

8 files changed

+148
-12
lines changed
Filter options

8 files changed

+148
-12
lines changed

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,13 +452,16 @@ public function load(array $configs, ContainerBuilder $container)
452452
$this->registerSecretsConfiguration($config['secrets'], $container, $loader);
453453

454454
$container->getDefinition('exception_listener')->replaceArgument(3, $config['exceptions']);
455+
$container->getDefinition('error_handler.error_renderer.html')->replaceArgument(6, $config['exceptions']);
455456

456457
if ($this->isConfigEnabled($container, $config['serializer'])) {
457458
if (!class_exists(\Symfony\Component\Serializer\Serializer::class)) {
458459
throw new LogicException('Serializer support cannot be enabled as the Serializer component is not installed. Try running "composer require symfony/serializer-pack".');
459460
}
460461

461462
$this->registerSerializerConfiguration($config['serializer'], $container, $loader);
463+
464+
$container->getDefinition('error_handler.error_renderer.serializer')->replaceArgument(4, $config['exceptions']);
462465
}
463466

464467
if ($propertyInfoEnabled) {

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/error_renderer.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
->factory([HtmlErrorRenderer::class, 'getAndCleanOutputBuffer'])
3131
->args([service('request_stack')]),
3232
service('logger')->nullOnInvalid(),
33+
abstract_arg('Configuration per exception class'),
3334
])
3435

3536
->alias('error_renderer.html', 'error_handler.error_renderer.html')

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@
208208
inline_service()
209209
->factory([HtmlErrorRenderer::class, 'isDebug'])
210210
->args([service('request_stack'), param('kernel.debug')]),
211+
abstract_arg('Configuration per exception class'),
211212
])
212213
;
213214

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@
106106
param('kernel.error_controller'),
107107
service('logger')->nullOnInvalid(),
108108
param('kernel.debug'),
109-
abstract_arg('an exceptions to log & status code mapping'),
109+
abstract_arg('Configuration per exception class'),
110110
])
111111
->tag('kernel.event_subscriber')
112112
->tag('monolog.logger', ['channel' => 'request'])

‎src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ErrorHandler/ErrorRenderer/HtmlErrorRenderer.php
+14-6Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,17 @@ class HtmlErrorRenderer implements ErrorRendererInterface
3939
private $projectDir;
4040
private $outputBuffer;
4141
private $logger;
42+
private $exceptionsMapping;
4243

4344
private static $template = 'views/error.html.php';
4445

4546
/**
46-
* @param bool|callable $debug The debugging mode as a boolean or a callable that should return it
47+
* @param bool|callable $debug The debugging mode as a boolean or a callable that should return it
4748
* @param string|FileLinkFormatter|null $fileLinkFormat
48-
* @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it
49+
* @param bool|callable $outputBuffer The output buffer as a string or a callable that should return it
50+
* @param array $exceptionsMapping Configuration per exception class
4951
*/
50-
public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null)
52+
public function __construct($debug = false, string $charset = null, $fileLinkFormat = null, string $projectDir = null, $outputBuffer = '', LoggerInterface $logger = null, array $exceptionsMapping = [])
5153
{
5254
if (!\is_bool($debug) && !\is_callable($debug)) {
5355
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
6365
$this->projectDir = $projectDir;
6466
$this->outputBuffer = $outputBuffer;
6567
$this->logger = $logger;
68+
$this->exceptionsMapping = $exceptionsMapping;
6669
}
6770

6871
/**
@@ -76,7 +79,14 @@ public function render(\Throwable $exception): FlattenException
7679
$headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine();
7780
}
7881

79-
$exception = FlattenException::createFromThrowable($exception, null, $headers);
82+
$statusCode = 500;
83+
foreach ($this->exceptionsMapping as $class => $config) {
84+
if ($exception instanceof $class && $config['status_code']) {
85+
$statusCode = $config['status_code'];
86+
break;
87+
}
88+
}
89+
$exception = FlattenException::createFromThrowable($exception, $statusCode, $headers);
8090

8191
return $exception->setAsString($this->renderException($exception));
8292
}
@@ -262,8 +272,6 @@ private function formatFile(string $file, int $line, string $text = null): strin
262272
* @param string $file A file path
263273
* @param int $line The selected line number
264274
* @param int $srcContext The number of displayed lines around or -1 for the whole file
265-
*
266-
* @return string
267275
*/
268276
private function fileExcerpt(string $file, int $line, int $srcContext = 3): string
269277
{

‎src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ErrorHandler/ErrorRenderer/SerializerErrorRenderer.php
+16-5Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,15 @@ class SerializerErrorRenderer implements ErrorRendererInterface
2828
private $format;
2929
private $fallbackErrorRenderer;
3030
private $debug;
31+
private $exceptionsMapping;
3132

3233
/**
33-
* @param string|callable(FlattenException) $format The format as a string or a callable that should return it
34-
* formats not supported by Request::getMimeTypes() should be given as mime types
35-
* @param bool|callable $debug The debugging mode as a boolean or a callable that should return it
34+
* @param string|callable(FlattenException) $format The format as a string or a callable that should return it
35+
* formats not supported by Request::getMimeTypes() should be given as mime types
36+
* @param bool|callable $debug The debugging mode as a boolean or a callable that should return it
37+
* @param array $exceptionsMapping Configuration per exception class
3638
*/
37-
public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false)
39+
public function __construct(SerializerInterface $serializer, $format, ErrorRendererInterface $fallbackErrorRenderer = null, $debug = false, array $exceptionsMapping = [])
3840
{
3941
if (!\is_string($format) && !\is_callable($format)) {
4042
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
4850
$this->format = $format;
4951
$this->fallbackErrorRenderer = $fallbackErrorRenderer ?? new HtmlErrorRenderer();
5052
$this->debug = $debug;
53+
$this->exceptionsMapping = $exceptionsMapping;
5154
}
5255

5356
/**
@@ -62,7 +65,15 @@ public function render(\Throwable $exception): FlattenException
6265
$headers['X-Debug-Exception-File'] = rawurlencode($exception->getFile()).':'.$exception->getLine();
6366
}
6467

65-
$flattenException = FlattenException::createFromThrowable($exception, null, $headers);
68+
$statusCode = 500;
69+
foreach ($this->exceptionsMapping as $class => $config) {
70+
if ($exception instanceof $class && $config['status_code']) {
71+
$statusCode = $config['status_code'];
72+
break;
73+
}
74+
}
75+
76+
$flattenException = FlattenException::createFromThrowable($exception, $statusCode, $headers);
6677

6778
try {
6879
$format = \is_string($this->format) ? $this->format : ($this->format)($flattenException);

‎src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/ErrorHandler/Tests/ErrorRenderer/HtmlErrorRendererTest.php
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
16+
use Symfony\Component\HttpFoundation\Response;
1617

1718
class HtmlErrorRendererTest extends TestCase
1819
{
@@ -40,6 +41,22 @@ public function getRenderData(): iterable
4041
<html>
4142
%A<title>An Error Occurred: Internal Server Error</title>
4243
%A<h2>The server returned a "500 Internal Server Error".</h2>%A
44+
HTML;
45+
46+
$expectedDebugWithStatusCode = <<<HTML
47+
<!-- Foo (418 I'm a teapot) -->
48+
<!DOCTYPE html>
49+
<html lang="en">
50+
%A<title>Foo (418 I'm a teapot)</title>
51+
%A<div class="trace trace-as-html" id="trace-box-1">%A
52+
<!-- Foo (418 I'm a teapot) -->
53+
HTML;
54+
55+
$expectedNonDebugWithStatusCode = <<<HTML
56+
<!DOCTYPE html>
57+
<html>
58+
%A<title>An Error Occurred: I'm a teapot</title>
59+
%A<h2>The server returned a "418 I'm a teapot".</h2>%A
4360
HTML;
4461

4562
yield '->render() returns the HTML content WITH stack traces in debug mode' => [
@@ -53,5 +70,27 @@ public function getRenderData(): iterable
5370
new HtmlErrorRenderer(false),
5471
$expectedNonDebug,
5572
];
73+
74+
yield '->render() returns the HTML content WITH stack traces in debug mode and contains the correct status code' => [
75+
new \RuntimeException('Foo'),
76+
new HtmlErrorRenderer(true, null, null, null, '', null, [
77+
\RuntimeException::class => [
78+
'status_code' => Response::HTTP_I_AM_A_TEAPOT,
79+
'log_level' => null,
80+
],
81+
]),
82+
$expectedDebugWithStatusCode,
83+
];
84+
85+
yield '->render() returns the HTML content WITHOUT stack traces in non-debug mode and contains the correct status code' => [
86+
new \RuntimeException('Foo'),
87+
new HtmlErrorRenderer(false, null, null, null, '', null, [
88+
\RuntimeException::class => [
89+
'status_code' => Response::HTTP_I_AM_A_TEAPOT,
90+
'log_level' => null,
91+
],
92+
]),
93+
$expectedNonDebugWithStatusCode,
94+
];
5695
}
5796
}
+73Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
/*
3+
* This file is part of the Symfony package.
4+
*
5+
* (c) Fabien Potencier <fabien@symfony.com>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
11+
namespace Symfony\Component\Serializer\Tests\ErrorRenderer;
12+
13+
use function json_decode;
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\ErrorHandler\ErrorRenderer\SerializerErrorRenderer;
16+
use Symfony\Component\HttpFoundation\Response;
17+
use Symfony\Component\Serializer\Encoder\JsonEncoder;
18+
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
19+
use Symfony\Component\Serializer\Serializer;
20+
21+
/**
22+
* @author Alexey Deriyenko <alexey.deriyenko@gmail.com>
23+
*/
24+
class SerializerErrorRendererTest extends TestCase
25+
{
26+
/**
27+
* @dataProvider getRenderData
28+
*/
29+
public function testRenderReturnsJson(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer)
30+
{
31+
$this->assertJson($serializerErrorRenderer->render($exception)->getAsString());
32+
}
33+
34+
/**
35+
* @dataProvider getRenderData
36+
*/
37+
public function testRenderReturnsJsonWithCorrectStatusCode(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer, int $expectedStatusCode)
38+
{
39+
$statusCodeFromJson = json_decode($serializerErrorRenderer->render($exception)->getAsString())->statusCode;
40+
$this->assertEquals($expectedStatusCode, $statusCodeFromJson);
41+
}
42+
43+
/**
44+
* @dataProvider getRenderData
45+
*/
46+
public function testRenderReturnsJsonWithCorrectStatusText(\Throwable $exception, SerializerErrorRenderer $serializerErrorRenderer, int $expectedStatusCode, string $expectedStatusText)
47+
{
48+
$statusTextFromJson = json_decode($serializerErrorRenderer->render($exception)->getAsString())->statusText;
49+
$this->assertEquals($expectedStatusText, $statusTextFromJson);
50+
}
51+
52+
public function getRenderData(): iterable
53+
{
54+
yield '->render() returns the JSON content without exception mapping config' => [
55+
new \RuntimeException('Foo'),
56+
new SerializerErrorRenderer(new Serializer([new ObjectNormalizer()], [new JsonEncoder()]), 'json'),
57+
Response::HTTP_INTERNAL_SERVER_ERROR,
58+
Response::$statusTexts[Response::HTTP_INTERNAL_SERVER_ERROR],
59+
];
60+
61+
yield '->render() returns the JSON content with exception mapping config' => [
62+
new \RuntimeException('Foo'),
63+
new SerializerErrorRenderer(new Serializer([new ObjectNormalizer()], [new JsonEncoder()]), 'json', null, false, [
64+
\RuntimeException::class => [
65+
'status_code' => Response::HTTP_I_AM_A_TEAPOT,
66+
'log_level' => null,
67+
],
68+
]),
69+
Response::HTTP_I_AM_A_TEAPOT,
70+
Response::$statusTexts[Response::HTTP_I_AM_A_TEAPOT],
71+
];
72+
}
73+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.