<?php
namespace LaunchPad\Bundle\LaunchPadBundle\Base\EventListener;
use LaunchPad\Bundle\LaunchPadBundle\Base\Exception\AppClientException;
use LaunchPad\Bundle\LaunchPadBundle\Base\Exception\MissingApiParamsException;
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\KernelInterface;
use Throwable;
class AppExceptionListener
{
/**
* @var Container
*/
private $container;
/**
* @param ContainerInterface $container
*/
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Intercept exceptions
*
* @param ExceptionEvent $event
* @return void
* @throws \Exception
*/
public function onKernelException(ExceptionEvent $event): void
{
$exception = $event->getThrowable();
if (!$this->isApiCall($event)) {
return;
}
$responseData = [
'success' => false,
'data' => null,
'errors' => $exception instanceof MissingApiParamsException
? $exception->getErrors()
: [],
'message' => $this->getApiExceptionMessage($exception),
'code' => $exception->getCode() ?: 500,
];
if ($this->inDebugMode()) {
$responseData['debug'] = [
'trace' => $exception->getTraceAsString(),
'message' => $exception->getMessage(),
];
}
$response = new JsonResponse($responseData);
$response->headers->set('Content-Type', 'application/json');
$response->setStatusCode($this->getHttpCodeForException($exception));
$event->allowCustomResponseCode();
$event->setResponse($response);
}
/**
* Get api exception message - different for def and prod environments
*
* @param Throwable $exception
* @return string
* @throws \Exception
*/
private function getApiExceptionMessage(Throwable $exception): string
{
return $exception instanceof AppClientException || $this->inDebugMode()
? $exception->getMessage()
: 'An error ocurred, please try again later';
}
/**
* Check if exception occurred during API call
*
* @param ExceptionEvent $event
* @return bool
*/
private function isApiCall(ExceptionEvent $event): bool
{
$request = $event->getRequest();
if (strpos($request->get('_controller'), '\\Api\\') !== false) {
return true;
}
return strpos($request->headers->get('Content-Type'), '/json') !== false;
}
/**
* Check if in debug mode
*
* @return bool
* @throws \Exception
*/
private function inDebugMode(): bool
{
if ($this->container->hasParameter('debug')
&& $this->container->getParameter('debug') === true
) {
return true;
}
/** @var KernelInterface $kernel */
$kernel = $this->container->get('kernel');
return $kernel->getEnvironment() === 'dev';
}
/**
* Get HTTP code for exception
*
* @param Throwable $exception
* @return int
*/
private function getHttpCodeForException(Throwable $exception): int
{
if ($exception instanceof AppClientException) {
return 200;
}
if ($exception instanceof AccessDeniedHttpException) {
return 403;
}
return 500;
}
}