<?php
namespace App\EventListener;
use App\Exception\ApiAccessDeniedException;
use App\Exception\VerboseExceptionInterface;
use Symfony\Component\ErrorHandler\Exception\FlattenException;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\KernelInterface;
class ExceptionListener implements EventSubscriberInterface
{
/**
* @var KernelInterface
*/
private $kernel;
public function __construct(KernelInterface $kernel)
{
$this->kernel = $kernel;
}
public static function getSubscribedEvents(): array
{
return [
KernelEvents::EXCEPTION => 'onKernelException',
];
}
public function onKernelException(ExceptionEvent $event): void
{
if (!$event->isMasterRequest()) {
return;
}
if (!$event->getRequest() instanceof Request || $event->getRequest()->getRequestFormat() !== 'json') {
return;
}
$exception = $event->getThrowable();
$response = [
'error' => '',
'code' => Response::HTTP_BAD_REQUEST,
'message' => null,
];
$exceptionInterfaces = class_implements(\get_class($exception));
if (
isset($exceptionInterfaces[HttpExceptionInterface::class]) &&
method_exists($exception, 'getStatusCode')
) {
// our exception is a HttpException, get status code from it
/* @var HttpExceptionInterface $exception */
$response['code'] = $exception->getStatusCode();
if (
$exception instanceof NotFoundHttpException &&
strpos($exception->getMessage(), 'No route found') !== false
) {
// Invalid route.
$response['error'] = 'Invalid request.';
} elseif ($exception instanceof AccessDeniedHttpException || $exception instanceof ApiAccessDeniedException) {
$response['error'] = $exception->getMessage() === 'Token issue' ? $exception->getMessage() : 'Not allowed, insufficient permissions.';
$response['message'] = $exception->getMessage();
}
} else {
$response['code'] = $exception->getCode() === 0 ? Response::HTTP_INTERNAL_SERVER_ERROR : $exception->getCode();
if (!$this->kernel->isDebug()) {
$response['error'] = '';
}
}
if ($response['error'] === '' && isset(Response::$statusTexts[$response['code']])) {
$response['error'] = Response::$statusTexts[$response['code']];
}
if (
isset($exceptionInterfaces[VerboseExceptionInterface::class]) &&
method_exists($exception, 'getExtraData')
) {
/* @var VerboseExceptionInterface $exception */
$response['errors'] = $exception->getExtraData();
}
if ($exception instanceof ConflictHttpException) {
$response['message'] = $exception->getMessage();
$response['error'] = $exception->getMessage();
}
if ($exception instanceof BadRequestHttpException) {
$response['message'] = $exception->getMessage();
$response['error'] = $exception->getMessage();
}
// give some more feedback in debug mode :-)
if ($this->kernel->isDebug()) {
$response['debug'] = [
'line' => $exception->getLine(),
'file' => $exception->getFile(),
'exception' => FlattenException::createFromThrowable($exception)->toArray(),
];
}
if ($this->kernel->isDebug()) {
unset($response['message']);
}
$event->setResponse(new JsonResponse($response, $response['code']));
}
}