diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index eb0e3c1cb44e0..45761dfbbed42 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -5,6 +5,7 @@ CHANGELOG --- * Add method `isKernelTerminating()` to `ExceptionEvent` that allows to check if an exception was thrown while the kernel is being terminated + * Add `HttpException::fromStatusCode()` 7.0 --- diff --git a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php index 444be1b3fe7d2..85e94c235de12 100644 --- a/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php +++ b/src/Symfony/Component/HttpKernel/Controller/ArgumentResolver/RequestPayloadValueResolver.php @@ -13,13 +13,14 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Attribute\MapQueryString; use Symfony\Component\HttpKernel\Attribute\MapRequestPayload; use Symfony\Component\HttpKernel\Controller\ValueResolverInterface; use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent; +use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; use Symfony\Component\HttpKernel\Exception\HttpException; +use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\Serializer\Exception\NotEncodableValueException; use Symfony\Component\Serializer\Exception\PartialDenormalizationException; @@ -124,13 +125,13 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo } if (\count($violations)) { - throw new HttpException($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($payload, $violations)); + throw HttpException::fromStatusCode($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), iterator_to_array($violations))), new ValidationFailedException($payload, $violations)); } } else { try { $payload = $this->$payloadMapper($request, $type, $argument); } catch (PartialDenormalizationException $e) { - throw new HttpException($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), $e->getErrors())), $e); + throw HttpException::fromStatusCode($validationFailedCode, implode("\n", array_map(static fn ($e) => $e->getMessage(), $e->getErrors())), $e); } } @@ -138,7 +139,7 @@ public function onKernelControllerArguments(ControllerArgumentsEvent $event): vo $payload = match (true) { $argument->metadata->hasDefaultValue() => $argument->metadata->getDefaultValue(), $argument->metadata->isNullable() => null, - default => throw new HttpException($validationFailedCode) + default => throw HttpException::fromStatusCode($validationFailedCode) }; } @@ -167,11 +168,11 @@ private function mapQueryString(Request $request, string $type, MapQueryString $ private function mapRequestPayload(Request $request, string $type, MapRequestPayload $attribute): ?object { if (null === $format = $request->getContentTypeFormat()) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, 'Unsupported format.'); + throw new UnsupportedMediaTypeHttpException('Unsupported format.'); } if ($attribute->acceptFormat && !\in_array($format, (array) $attribute->acceptFormat, true)) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, sprintf('Unsupported format, expects "%s", but "%s" given.', implode('", "', (array) $attribute->acceptFormat), $format)); + throw new UnsupportedMediaTypeHttpException(sprintf('Unsupported format, expects "%s", but "%s" given.', implode('", "', (array) $attribute->acceptFormat), $format)); } if ($data = $request->request->all()) { @@ -183,15 +184,15 @@ private function mapRequestPayload(Request $request, string $type, MapRequestPay } if ('form' === $format) { - throw new HttpException(Response::HTTP_BAD_REQUEST, 'Request payload contains invalid "form" data.'); + throw new BadRequestHttpException('Request payload contains invalid "form" data.'); } try { return $this->serializer->deserialize($data, $type, $format, self::CONTEXT_DESERIALIZE + $attribute->serializationContext); } catch (UnsupportedFormatException $e) { - throw new HttpException(Response::HTTP_UNSUPPORTED_MEDIA_TYPE, sprintf('Unsupported format: "%s".', $format), $e); + throw new UnsupportedMediaTypeHttpException(sprintf('Unsupported format: "%s".', $format), $e); } catch (NotEncodableValueException $e) { - throw new HttpException(Response::HTTP_BAD_REQUEST, sprintf('Request payload contains invalid "%s" data.', $format), $e); + throw new BadRequestHttpException(sprintf('Request payload contains invalid "%s" data.', $format), $e); } } } diff --git a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php index cf52851928f86..6e47baf2ee6c3 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/ErrorListener.php @@ -63,7 +63,7 @@ public function logKernelException(ExceptionEvent $event): void } if (!$throwable instanceof HttpExceptionInterface || $throwable->getStatusCode() !== $config['status_code']) { $headers = $throwable instanceof HttpExceptionInterface ? $throwable->getHeaders() : []; - $throwable = new HttpException($config['status_code'], $throwable->getMessage(), $throwable, $headers); + $throwable = HttpException::fromStatusCode($config['status_code'], $throwable->getMessage(), $throwable, $headers); $event->setThrowable($throwable); } break; @@ -78,7 +78,7 @@ public function logKernelException(ExceptionEvent $event): void /** @var WithHttpStatus $instance */ $instance = $attributes[0]->newInstance(); - $throwable = new HttpException($instance->statusCode, $throwable->getMessage(), $throwable, $instance->headers); + $throwable = HttpException::fromStatusCode($instance->statusCode, $throwable->getMessage(), $throwable, $instance->headers); $event->setThrowable($throwable); break; } diff --git a/src/Symfony/Component/HttpKernel/Exception/HttpException.php b/src/Symfony/Component/HttpKernel/Exception/HttpException.php index f95f77bcafae9..7eaf049e9302c 100644 --- a/src/Symfony/Component/HttpKernel/Exception/HttpException.php +++ b/src/Symfony/Component/HttpKernel/Exception/HttpException.php @@ -29,6 +29,27 @@ public function __construct(int $statusCode, string $message = '', \Throwable $p parent::__construct($message, $code, $previous); } + public static function fromStatusCode(int $statusCode, string $message = '', \Throwable $previous = null, array $headers = [], int $code = 0): self + { + return match ($statusCode) { + 400 => new BadRequestHttpException($message, $previous, $code, $headers), + 403 => new AccessDeniedHttpException($message, $previous, $code, $headers), + 404 => new NotFoundHttpException($message, $previous, $code, $headers), + 406 => new NotAcceptableHttpException($message, $previous, $code, $headers), + 409 => new ConflictHttpException($message, $previous, $code, $headers), + 410 => new GoneHttpException($message, $previous, $code, $headers), + 411 => new LengthRequiredHttpException($message, $previous, $code, $headers), + 412 => new PreconditionFailedHttpException($message, $previous, $code, $headers), + 423 => new LockedHttpException($message, $previous, $code, $headers), + 415 => new UnsupportedMediaTypeHttpException($message, $previous, $code, $headers), + 422 => new UnprocessableEntityHttpException($message, $previous, $code, $headers), + 428 => new PreconditionRequiredHttpException($message, $previous, $code, $headers), + 429 => new TooManyRequestsHttpException(null, $message, $previous, $code, $headers), + 503 => new ServiceUnavailableHttpException(null, $message, $previous, $code, $headers), + default => new static($statusCode, $message, $previous, $headers, $code), + }; + } + public function getStatusCode(): int { return $this->statusCode; diff --git a/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php b/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php index fad9e796f439b..11636bbb60bd5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Exception/HttpExceptionTest.php @@ -63,6 +63,38 @@ public function testThrowableIsAllowedForPrevious() $this->assertSame($previous, $exception->getPrevious()); } + /** + * @dataProvider provideStatusCode + */ + public function testFromStatusCode(int $statusCode) + { + $exception = HttpException::fromStatusCode($statusCode); + $this->assertInstanceOf(HttpException::class, $exception); + $this->assertSame($statusCode, $exception->getStatusCode()); + } + + public static function provideStatusCode() + { + return [ + [400], + [401], + [403], + [404], + [406], + [409], + [410], + [411], + [412], + [418], + [423], + [415], + [422], + [428], + [429], + [503], + ]; + } + protected function createException(string $message = '', \Throwable $previous = null, int $code = 0, array $headers = []): HttpException { return new HttpException(200, $message, $previous, $headers, $code);
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: