<?php declare(strict_types=1);
namespace App\Framework\EventListener;
use App\Framework\Exception\APIException;
use App\Framework\Exception\AssertionException;
use App\Framework\Exception\TalloException;
use Doctrine\ORM\EntityNotFoundException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class APIExceptionEventListener
{
private string $env;
public function __construct(string $env)
{
$this->env = $env;
}
public function onException(ExceptionEvent $event): void
{
$debug = true;
// TODO: Any exception that is not a user-facing error should be logged and not sent in production environment!!!!
// Only intercept requests that are accepting json as response
$request = $event->getRequest();
if (!in_array('application/json', $request->getAcceptableContentTypes(), true)) {
return;
}
$statusCode = 500;
$result = [];
$exception = $event->getThrowable();
// Handle API Exceptions more gracefully, as 400 (Client errors)
if ($exception instanceof APIException) {
$statusCode = 400;
$result = [
'type' => $exception->getMessage(),
'errors' => $exception->getErrors(),
];
if ($debug) {
$result['debug'] = $exception->getDebugData();
}
} elseif ($exception instanceof EntityNotFoundException) {
$statusCode = 400;
$result = [
'code' => 'error.entity.notfound',
'message' => $exception->getMessage(),
];
} elseif ($exception instanceof AssertionException) {
$statusCode = 500;
$result = [
'type' => 'assert.failed',
'errors' => $exception->getErrors(),
];
} elseif ($exception instanceof AccessDeniedHttpException) {
$statusCode = 403;
$result = [
'type' => 'error',
'code' => 'error.authorization.rejected',
'message' => $exception->getCode(),
'errors' => $exception->getMessage(),
];
} elseif ($exception instanceof TalloException) {
$statusCode = 400;
$result = [
'type' => 'error',
'code' => $exception->getCode(),
'message' => $exception->getCode(),
'errors' => $exception->getErrors(),
];
} else {
// DEBUG mode?
$statusCode = 500;
$result = [
'type' => 'Error',
'code' => $exception->getCode(),
'message' => $exception->getMessage(),
];
}
// Add the stack trace in debug
if ($this->env === 'dev') {
$result['trace'] = $exception->getTraceAsString();
}
$response = new JsonResponse($result, $statusCode);
$response->setEncodingOptions(JSON_UNESCAPED_UNICODE);
$event->setResponse($response);
}
}