From 93cee037734484927a59ddca4160b7052fb7477e Mon Sep 17 00:00:00 2001 From: Dejan Angelov Date: Thu, 5 Jan 2023 19:14:31 +0100 Subject: [PATCH 1/2] [HttpKernel] Allow dynamic header values within the WithHttpStatus attribute --- .../DependencyInjection/FrameworkExtension.php | 4 ++++ .../FrameworkBundle/Resources/config/web.php | 9 +++++++++ src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../HttpKernel/EventListener/ErrorListener.php | 13 +++++++++++-- .../Tests/EventListener/ErrorListenerTest.php | 18 +++++++++++++++++- src/Symfony/Component/HttpKernel/composer.json | 3 ++- 6 files changed, 44 insertions(+), 4 deletions(-) diff --git a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php index bcd8aff6932e2..44faa5c678e63 100644 --- a/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php +++ b/src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php @@ -280,6 +280,10 @@ public function load(array $configs, ContainerBuilder $container) $container->removeDefinition('argument_resolver.backed_enum_resolver'); } + if (!class_exists(ExpressionLanguage::class)) { + $container->removeDefinition('exception_listener.expression_language'); + } + $loader->load('services.php'); $loader->load('fragment_renderer.php'); $loader->load('error_renderer.php'); diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php index cd4cdffa10ade..055a8e1120b5d 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/web.php @@ -12,6 +12,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use Symfony\Bundle\FrameworkBundle\Controller\ControllerResolver; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpKernel\Controller\ArgumentResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\BackedEnumValueResolver; use Symfony\Component\HttpKernel\Controller\ArgumentResolver\DateTimeValueResolver; @@ -118,10 +119,18 @@ service('logger')->nullOnInvalid(), param('kernel.debug'), abstract_arg('an exceptions to log & status code mapping'), + service('exception_listener.expression_language')->nullOnInvalid(), ]) ->tag('kernel.event_subscriber') ->tag('monolog.logger', ['channel' => 'request']) + ->set('exception_listener.expression_language', ExpressionLanguage::class) + ->args([service('cache.exception_listener_expression_language')->nullOnInvalid()]) + + ->set('cache.exception_listener_expression_language') + ->parent('cache.system') + ->tag('cache.pool') + ->set('controller.cache_attribute_listener', CacheAttributeListener::class) ->tag('kernel.event_subscriber') diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 650bd9bc75b49..3ce38655fd57f 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -9,6 +9,7 @@ CHANGELOG * Add `#[WithHttpStatus]` for defining status codes for exceptions * Use an instance of `Psr\Clock\ClockInterface` to generate the current date time in `DateTimeValueResolver` * Add `#[WithLogLevel]` for defining log levels for exceptions + * Allow dynamic values for headers specified within `#[HttpStatus]` 6.2 --- diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index 2e8b75afcf585..5aae22a63475c 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -15,6 +15,8 @@ use Psr\Log\LogLevel; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Attribute\WithHttpStatus; use Symfony\Component\HttpKernel\Attribute\WithLogLevel; @@ -39,16 +41,18 @@ class ErrorListener implements EventSubscriberInterface * @var array|null}> */ protected $exceptionsMapping; + protected $expressionLanguage; /** * @param array|null}> $exceptionsMapping */ - public function __construct(string|object|array|null $controller, LoggerInterface $logger = null, bool $debug = false, array $exceptionsMapping = []) + public function __construct(string|object|array|null $controller, LoggerInterface $logger = null, bool $debug = false, array $exceptionsMapping = [], ExpressionLanguage $expressionLanguage = null) { $this->controller = $controller; $this->logger = $logger; $this->debug = $debug; $this->exceptionsMapping = $exceptionsMapping; + $this->expressionLanguage = $expressionLanguage; } public function logKernelException(ExceptionEvent $event) @@ -76,8 +80,13 @@ public function logKernelException(ExceptionEvent $event) if ($attributes) { /** @var WithHttpStatus $instance */ $instance = $attributes[0]->newInstance(); + $headers = $instance->headers; - $throwable = new HttpException($instance->statusCode, $throwable->getMessage(), $throwable, $instance->headers); + foreach ($headers as $header => $value) { + $headers[$header] = $value instanceof Expression ? $this->expressionLanguage->evaluate($value, ['this' => $throwable]) : $value; + } + + $throwable = new HttpException($instance->statusCode, $throwable->getMessage(), $throwable, $headers); $event->setThrowable($throwable); } } diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php index 64068ee5d3fa8..f9c4447885865 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php @@ -16,6 +16,8 @@ use Psr\Log\LogLevel; use Symfony\Component\ErrorHandler\Exception\FlattenException; use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\ExpressionLanguage\ExpressionLanguage; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\WithHttpStatus; @@ -161,7 +163,7 @@ public function testHandleHttpAttribute(\Throwable $exception, int $expectedStat { $request = new Request(); $event = new ExceptionEvent(new TestKernel(), $request, HttpKernelInterface::MAIN_REQUEST, $exception); - $l = new ErrorListener('not used'); + $l = new ErrorListener('not used', expressionLanguage: new ExpressionLanguage()); $l->logKernelException($event); $l->onKernelException($event); @@ -288,6 +290,7 @@ public static function exceptionWithAttributeProvider() { yield [new WithCustomUserProvidedAttribute(), 208, ['name' => 'value']]; yield [new WithGeneralAttribute(), 412, ['some' => 'thing']]; + yield [new WithDynamicHeader('Example'), 404, ['name' => 'The name is Example.']]; } } @@ -353,3 +356,16 @@ class WithGeneralAttribute extends \Exception class WarningWithLogLevelAttribute extends \Exception { } + +#[HttpStatus( + statusCode: 404, + headers: [ + 'name' => new Expression('"The name is " ~ this.name ~ "."'), + ] +)] +class WithDynamicHeader extends \Exception +{ + public function __construct(public readonly string $name) + { + } +} diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 8193edbe8c28c..3394b8b0e076a 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -69,7 +69,8 @@ "symfony/browser-kit": "", "symfony/config": "", "symfony/console": "", - "symfony/dependency-injection": "" + "symfony/dependency-injection": "", + "symfony/expression-language": "For setting dynamic header values within the WithHttpStatus attribute" }, "autoload": { "psr-4": { "Symfony\\Component\\HttpKernel\\": "" }, From 71d5b22d86d0da2be2b1542bdd45207322a013fb Mon Sep 17 00:00:00 2001 From: Dejan Angelov Date: Fri, 6 Jan 2023 16:47:35 +0100 Subject: [PATCH 2/2] [HttpKernel] Use the new name of the attribute --- src/Symfony/Component/HttpKernel/CHANGELOG.md | 2 +- .../HttpKernel/Tests/EventListener/ErrorListenerTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index 3ce38655fd57f..a779006de3202 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -9,7 +9,7 @@ CHANGELOG * Add `#[WithHttpStatus]` for defining status codes for exceptions * Use an instance of `Psr\Clock\ClockInterface` to generate the current date time in `DateTimeValueResolver` * Add `#[WithLogLevel]` for defining log levels for exceptions - * Allow dynamic values for headers specified within `#[HttpStatus]` + * Allow dynamic values for headers specified within `#[WithHttpStatus]` 6.2 --- diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php index f9c4447885865..64d7a381f99f6 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/ErrorListenerTest.php @@ -357,7 +357,7 @@ class WarningWithLogLevelAttribute extends \Exception { } -#[HttpStatus( +#[WithHttpStatus( statusCode: 404, headers: [ 'name' => new Expression('"The name is " ~ this.name ~ "."'),