Issues (97)

src/EventSubscriber/ApiExceptionSubscriber.php (1 issue)

Severity
1
<?php
2
3
namespace App\EventSubscriber;
4
5
use App\Response\ErrorResponse;
6
use App\Response\Violation;
7
use App\Response\ViolationListResponse;
8
use App\Rest\ValidationFailedException;
9
use JMS\Serializer\SerializerInterface;
10
use Psr\Log\LoggerInterface;
11
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
12
use Symfony\Component\HttpFoundation\Response;
13
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
14
use Symfony\Component\HttpKernel\Exception\HttpException;
15
use Symfony\Component\HttpKernel\KernelEvents;
16
17
class ApiExceptionSubscriber implements EventSubscriberInterface {
18
19
    private const JsonContentType = 'application/json';
20
21
    public function __construct(private readonly SerializerInterface $serializer, private readonly LoggerInterface $logger)
22
    {
23
    }
24 14
25 14
    public function onKernelException(ExceptionEvent $event) {
26 14
        $request = $event->getRequest();
27 14
        $acceptable = $request->getAcceptableContentTypes();
0 ignored issues
show
The assignment to $acceptable is dead and can be removed.
Loading history...
28
29 1
        if(!in_array(self::JsonContentType, $request->getAcceptableContentTypes())) {
30 1
            return;
31
        }
32 1
33 1
        $throwable = $event->getThrowable();
34
        $code = Response::HTTP_INTERNAL_SERVER_ERROR;
35
36
        // Case 1: general HttpException (Authorization/Authentication) or BadRequest
37
        if($throwable instanceof HttpException) {
38
            $code = $throwable->getStatusCode();
39
            $message = new ErrorResponse($throwable->getMessage(), $throwable::class);
40
        } else if($throwable instanceof ValidationFailedException) { // Case 2: validation failed
41
            $code = Response::HTTP_BAD_REQUEST;
42
43
            $violations = [ ];
44
            foreach($throwable->getConstraintViolations() as $violation) {
45
                $violations[] = new Violation($violation->getPropertyPath(), (string)$violation->getMessage());
46
            }
47
48
            $message = new ViolationListResponse($violations);
49
        } else { // Case 3: General error
50
            $message = new ErrorResponse(
51
                'An unknown error occured.',
52
                $throwable::class
53
            );
54
55
            $this->logger->error($throwable->getMessage(), [
56
                'e' => $throwable
57
            ]);
58
        }
59
60
        $validStatusCodes = array_keys(Response::$statusTexts);
61
        if(!in_array($code, $validStatusCodes)) {
62
            $code = Response::HTTP_INTERNAL_SERVER_ERROR;
63
        }
64
65
        $response = new Response(
66
            $this->serializer->serialize($message, 'json'),
67
            $code,
68
            [
69
                'Content-Type' => 'application/json'
70
            ]
71
        );
72
73
        $event->setResponse($response);
74
    }
75
76
    /**
77
     * @inheritDoc
78
     */
79
    public static function getSubscribedEvents(): array {
80
        return [
81
            KernelEvents::EXCEPTION => 'onKernelException'
82
        ];
83
    }
84
}