src/Subscriber/ExceptionSubscriber.php line 85

Open in your IDE?
  1. <?php
  2. /**
  3.  * Listener class for exception handling
  4.  *
  5.  * PHP version 7.4
  6.  *
  7.  * @category   App
  8.  * @package    App\Listener
  9.  * @author     Momcilo Radotic <m.radotic@outlook.com>
  10.  * @copyright  2021 MoravaPro
  11.  * @license    MoravaPro
  12.  */
  13. namespace App\Subscriber;
  14. use App\Model\ResponseModel;
  15. use Psr\Log\LoggerInterface;
  16. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  17. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  18. use Symfony\Component\HttpFoundation\Exception\BadRequestException;
  19. use Symfony\Component\HttpFoundation\JsonResponse;
  20. use Symfony\Component\HttpFoundation\Request;
  21. use Symfony\Component\HttpFoundation\Response;
  22. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  23. use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
  24. use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
  25. use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
  26. use Symfony\Component\HttpKernel\KernelEvents;
  27. use Symfony\Component\Routing\Exception\MethodNotAllowedException;
  28. use Symfony\Component\Security\Core\Exception\AccessDeniedException;
  29. use Twig\Environment;
  30. /**
  31.  * Listener class for exception handling
  32.  *
  33.  * @category   App
  34.  * @package    App\Listener
  35.  */
  36. class ExceptionSubscriber implements EventSubscriberInterface
  37. {
  38.     /**
  39.      * @var ParameterBagInterface
  40.      */
  41.     private ParameterBagInterface $parameterBag;
  42.     /**
  43.      * @var LoggerInterface
  44.      */
  45.     private LoggerInterface $logger;
  46.     /**
  47.      * @var Environment
  48.      */
  49.     private Environment $twigEnvironment;
  50.     public function __construct(ParameterBagInterface $parameterBagLoggerInterface $loggerEnvironment $twigEnvironment)
  51.     {
  52.         $this->parameterBag $parameterBag;
  53.         $this->logger $logger;
  54.         $this->twigEnvironment $twigEnvironment;
  55.     }
  56.     /**
  57.      * {@inheritDoc}
  58.      */
  59.     public static function getSubscribedEvents()
  60.     {
  61.         return [
  62.             KernelEvents::EXCEPTION => [
  63.                 [
  64.                     'onKernelException',
  65.                     10
  66.                 ]
  67.             ]
  68.         ];
  69.     }
  70.     /**
  71.      * Handle application exception
  72.      *
  73.      * @param ExceptionEvent $event
  74.      */
  75.     public function onKernelException(ExceptionEvent $event)
  76.     {
  77.         try {
  78.             // if environment dev, do nothing, display app error
  79.             if ($this->parameterBag->get('kernel.environment') == 'dev') {
  80.                 return ;
  81.             }
  82.         } catch (\Throwable $e) {
  83.             $this->logger->error($e);
  84.         }
  85.         $exception $event->getThrowable();
  86.         $this->logger->error($exception);
  87.         $isAjaxOrApi $this->checkIsAjaxOrApi($event->getRequest());
  88.         switch (get_class($exception)) {
  89.             case AccessDeniedException::class:
  90.                 $response $isAjaxOrApi
  91.                     $this->handleJsonErrorResponse(Response::HTTP_UNAUTHORIZED'Access denied')
  92.                     : $this->handleHttpErrorResponse(Response::HTTP_UNAUTHORIZED);
  93.                 break;
  94.             case BadRequestException::class:
  95.             case BadRequestHttpException::class:
  96.                 $response $isAjaxOrApi
  97.                     $this->handleJsonErrorResponse(Response::HTTP_BAD_REQUEST'Bad request data')
  98.                     : $this->handleHttpErrorResponse(Response::HTTP_BAD_REQUEST);
  99.                 break;
  100.             case NotFoundHttpException::class:
  101.             case MethodNotAllowedException::class:
  102.             case MethodNotAllowedHttpException::class:
  103.                 $response $isAjaxOrApi
  104.                     $this->handleJsonErrorResponse(Response::HTTP_NOT_FOUND'Not found')
  105.                     : $this->handleHttpErrorResponse(Response::HTTP_NOT_FOUND);
  106.                 break;
  107.             default:
  108.                 $response $isAjaxOrApi
  109.                     $this->handleJsonErrorResponse(Response::HTTP_INTERNAL_SERVER_ERROR'Internal server error')
  110.                     : $this->handleHttpErrorResponse(Response::HTTP_INTERNAL_SERVER_ERROR);
  111.         }
  112.         $event->setResponse($response);
  113.     }
  114.     /**
  115.      * Check type of request
  116.      *
  117.      * @param Request $request
  118.      *
  119.      * @return bool
  120.      */
  121.     private function checkIsAjaxOrApi(Request $request)
  122.     {
  123.         $accepts $request->get('Accept');
  124.         $contentType $request->headers->get('Content-Type');
  125.         $wantsJson preg_match('{^\s*application/json,\s*}'$accepts) || preg_match('{^\s*application/json\s*}'$contentType);
  126.         $isAjax $request->isXmlHttpRequest();
  127.         return $wantsJson || $isAjax;
  128.     }
  129.     /**
  130.      * Handle json error response
  131.      *
  132.      * @param int $errorCode
  133.      * @param string $errorMessage
  134.      *
  135.      * @return JsonResponse
  136.      */
  137.     private function handleJsonErrorResponse(int $errorCodestring $errorMessage) : JsonResponse
  138.     {
  139.         $response = new ResponseModel();
  140.         $response->setError($errorCode$errorMessage);
  141.         $jsonResponse = new JsonResponse();
  142.         $jsonResponse->setJson(json_encode($response));
  143.         $jsonResponse->setStatusCode($errorCode);
  144.         return $jsonResponse;
  145.     }
  146.     /**
  147.      * Handle http exception
  148.      *
  149.      * @param int $errorCode
  150.      *
  151.      * @return Response
  152.      */
  153.     private function handleHttpErrorResponse(int $errorCode) : Response
  154.     {
  155.         $httpResponse = new Response();
  156.         $httpResponse->setStatusCode($errorCode);
  157.         $httpResponse->setContent($this->renderErrorPage($errorCode));
  158.         return $httpResponse;
  159.     }
  160.     /**
  161.      * Render error html page
  162.      *
  163.      * @param int $errorCode
  164.      *
  165.      * @return string
  166.      */
  167.     private function renderErrorPage(int $errorCode) : string
  168.     {
  169.         try {
  170.             switch ($errorCode) {
  171.                 case Response::HTTP_UNAUTHORIZED:
  172.                     return $this->twigEnvironment->render('unauthorized.html.twig');
  173.                 case Response::HTTP_BAD_REQUEST:
  174.                     return $this->twigEnvironment->render('bad_request.html.twig');
  175.                 case Response::HTTP_NOT_FOUND:
  176.                     return $this->twigEnvironment->render('not_found.html.twig');
  177.                 default:
  178.                     return $this->twigEnvironment->render('server_error.html.twig');
  179.             }
  180.         } catch (\Throwable $exception) {
  181.             $this->logger->error($exception);
  182.             return '<html><head></head><body><p>Internal server error</p></body></body></head></html>';//@TODO handle this case!! Create some static html page
  183.         }
  184.     }
  185. }