<?php
/**
* Author: Anton Orlov
* Date: 20.03.2018
* Time: 19:10
*/
namespace FMT\Application\Listener;
use FMT\Data\Entity\User;
use FMT\Infrastructure\Helper\LogHelper;
use FMT\Application\Controller\Common\NotFoundController;
use Stripe\Exception\ApiErrorException;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Templating\EngineInterface;
use Twig\Environment;
/**
* Class ExceptionListener
* @package FMT\Application\Listener
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
*/
class ExceptionListener
{
/** @var Environment */
private $engine;
/** @var bool */
private $debug;
/** @var RouterInterface */
private $router;
/** @var TokenStorageInterface */
private $tokenStorage;
public function __construct(
Environment $engine,
$debug,
RouterInterface $router,
TokenStorageInterface $tokenStorage
) {
$this->engine = $engine;
$this->debug = (bool) $debug;
$this->router = $router;
$this->tokenStorage = $tokenStorage;
}
/**
* @param ExceptionEvent $event
*/
public function onResponseFormatException(ExceptionEvent $event)
{
$request = $event->getRequest();
$exception = $event->getThrowable();
$details = [
"success" => false,
"code" => 500,
"host" => $request->getHost(),
"message" => $exception->getMessage()
];
if ($this->debug) {
$details["exception"] = get_class($exception);
$details["backtrace"] = $exception->getTraceAsString();
}
if ($exception instanceof HttpException) {
$details["code"] = $exception->getStatusCode();
} elseif ($exception instanceof AccessDeniedException) {
$details["code"] = $this->tokenStorage->getToken()->getUser() instanceof User ?
Response::HTTP_FORBIDDEN :
Response::HTTP_UNAUTHORIZED;
} elseif ($exception instanceof ApiErrorException) {
$details["code"] = $exception->getHttpStatus();
}
LogHelper::error(
"[%s] %s |\nTrace: %s",
get_class($exception),
$exception->getMessage(),
$exception->getTraceAsString()
);
LogHelper::debug($exception->getTraceAsString());
$event->stopPropagation();
if ($this->isJsonResponse($request)) {
$event->setResponse(new JsonResponse($details));
return;
}
switch ($details['code']) {
case Response::HTTP_NOT_FOUND:
$route = $this->getRedirectToRoute(NotFoundController::ROUTE_404);
$response = new RedirectResponse($route);
break;
case Response::HTTP_UNAUTHORIZED:
header("Location: /log-in");
exit;
case Response::HTTP_FORBIDDEN:
case Response::HTTP_INTERNAL_SERVER_ERROR:
$template = $this->getTemplate($details);
$response = new Response($template);
break;
default:
$template = $this->engine->render('TwigBundle/views/Exception/error.html.twig', $details);
$response = new Response($template);
}
$event->setResponse($response);
}
/**
* @param $routeName
* @return string
*/
private function getRedirectToRoute($routeName)
{
return $this->router->generate($routeName);
}
/**
* @param array $details
* @return string
*/
private function getTemplate(array $details)
{
return $this->engine->render(
sprintf('@Public/errors/error%d.html.twig', $details['code']),
$details
);
}
/**
* @param Request $request
* @return bool
*/
private function isJsonResponse(Request $request)
{
$jsonHeades = array_filter([
$request->headers->get("X-Requested-With") === "XMLHttpRequest",
$request->getRequestFormat() === "json"
]);
return count($jsonHeades) > 1;
}
}