src/Ui/Http/Rest/EventSubscriber/ExceptionSubscriber.php line 35

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Ui\Http\Rest\EventSubscriber;
  4. use App\Shared\Domain\DomainException;
  5. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  6. use Symfony\Component\HttpFoundation\JsonResponse;
  7. use Symfony\Component\HttpFoundation\Response;
  8. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  9. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  10. use Symfony\Component\HttpKernel\KernelEvents;
  11. use Symfony\Component\Validator\ConstraintViolationInterface;
  12. use Symfony\Contracts\Translation\TranslatorInterface;
  13. use Throwable;
  14. use Ui\Http\Web\ConstraintCollection\Exception\ValidationException;
  15. final class ExceptionSubscriber implements EventSubscriberInterface
  16. {
  17.     public function __construct(
  18.         private TranslatorInterface $translator,
  19.         private string $environment,
  20.         private array $exceptionToStatus = []
  21.     ) {
  22.     }
  23.     public static function getSubscribedEvents() : array
  24.     {
  25.         return [
  26.             KernelEvents::EXCEPTION => 'onKernelException',
  27.         ];
  28.     }
  29.     public function onKernelException(ExceptionEvent $event) : void
  30.     {
  31.         $request $event->getRequest();
  32.         if (
  33.             'json' !== $request->getContentType()
  34.             && 'json' !== $request->getPreferredFormat()
  35.         ) {
  36.             return;
  37.         }
  38.         $exception $event->getThrowable();
  39.         $response = new JsonResponse();
  40.         $response->headers->set('Content-Type''application/vnd.api+json');
  41.         $response->setStatusCode($this->determineStatusCode($exception));
  42.         $response->setData($this->getErrorMessage($exception$response));
  43.         $event->setResponse($response);
  44.     }
  45.     private function getErrorMessage(Throwable $exceptionResponse $response) : array
  46.     {
  47.         $error = [
  48.             'error' => [
  49.                 'message' => $this->getExceptionMessage($exception$response),
  50.                 'code' => $this->determineStatusCode($exception),
  51.             ],
  52.         ];
  53.         if ($exception instanceof ValidationException) {
  54.             $constraints = [];
  55.             /** @var ConstraintViolationInterface $result */
  56.             foreach ($exception->getViolationList() as $result) {
  57.                 $constraints[$result->getPropertyPath()][] = $this->translator->trans($result->getMessage());
  58.             }
  59.             $error['error']['code'] = Response::HTTP_BAD_REQUEST;
  60.             $error['error']['errors'] = $constraints;
  61.         }
  62.         if ('dev' === $this->environment) {
  63.             $error \array_merge(
  64.                 $error,
  65.                 [
  66.                     'meta' => [
  67.                         'file' => $exception->getFile(),
  68.                         'line' => $exception->getLine(),
  69.                         'message' => $exception->getMessage(),
  70.                         'trace' => $exception->getTrace(),
  71.                         'traceString' => $exception->getTraceAsString(),
  72.                     ],
  73.                 ]
  74.             );
  75.         }
  76.         return $error;
  77.     }
  78.     private function getExceptionMessage(Throwable $exceptionResponse $response) : string
  79.     {
  80.         if ($exception instanceof DomainException) {
  81.             $response->setStatusCode($this->determineStatusCode($exception));
  82.             return $this->translator->trans(
  83.                 $exception->getMessage(),
  84.                 $exception->getParameters(),
  85.                 DomainException::TRANSLATION_DOMAIN
  86.             );
  87.         }
  88.         return $exception->getMessage();
  89.     }
  90.     private function determineStatusCode(Throwable $exception) : int
  91.     {
  92.         $exceptionClass \get_class($exception);
  93.         foreach ($this->exceptionToStatus as $class => $status) {
  94.             if (\is_a($exceptionClass$classtrue)) {
  95.                 return $status;
  96.             }
  97.         }
  98.         // Process HttpExceptionInterface after `exceptionToStatus` to allow overrides from config
  99.         if ($exception instanceof HttpExceptionInterface) {
  100.             return $exception->getStatusCode();
  101.         }
  102.         if ($exception instanceof DomainException) {
  103.             return $exception->getCode();
  104.         }
  105.         // Default status code is always 500
  106.         return Response::HTTP_INTERNAL_SERVER_ERROR;
  107.     }
  108. }