<?php
namespace AppBundle\Listener;
use AppBundle\Common\ExceptionPrintingToolkit;
use Codeages\Biz\Framework\Service\Exception\AccessDeniedException;
use Codeages\Biz\Framework\Service\Exception\NotFoundException;
use Symfony\Component\DependencyInjection\ContainerInterface;
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\HttpExceptionInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Topxia\Service\Common\ServiceKernel;
class ExceptionListener
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function onKernelException(ExceptionEvent $event)
{
$exception = $event->getThrowable();
$request = $event->getRequest();
$exception = $this->convertException($exception);
if (!$request->isXmlHttpRequest()) {
$this->setTargetPath($request);
$event->setThrowable($exception);
return;
}
$statusCode = $this->getStatusCode($exception);
$error = $this->getErrorDetails($exception, $statusCode);
$response = new JsonResponse(['error' => $error], $statusCode);
$event->setResponse($response);
}
protected function setTargetPath(Request $request)
{
if ($request->hasSession() && $request->isMethodSafe(false) && !$request->isXmlHttpRequest()) {
$request->getSession()->set('_target_path', $request->getUri());
}
}
private function getErrorDetails($exception, $statusCode)
{
$error = ['name' => 'Error'];
if ($this->container->get('kernel')->isDebug()) {
$error['message'] = $exception->getMessage();
$error['trace'] = ExceptionPrintingToolkit::printTraceAsArray($exception);
} elseif ($exception instanceof HttpExceptionInterface) {
$error['message'] = $exception->getMessage();
} else {
$error['message'] = 'Request occurs an error';
}
if (403 === $statusCode) {
$user = $this->getUser();
if ($user) {
return ['name' => 'AccessDenied', 'message' => $this->getServiceKernel()->trans('访问被拒绝!')];
}
return ['name' => 'Unlogin', 'message' => $this->getServiceKernel()->trans('当前操作,需要登录!')];
}
return $error;
}
public function getUser()
{
if (!$this->container->has('security.token_storage')) {
throw new \LogicException('The SecurityBundle is not registered in your application.');
}
if (null === $token = $this->container->get('security.token_storage')->getToken()) {
return null;
}
if (!is_object($user = $token->getUser())) {
return null;
}
return $user;
}
private function convertException($exception)
{
if ($exception instanceof AccessDeniedException) {
return new AccessDeniedHttpException($exception->getMessage(), $exception);
}
if ($exception instanceof NotFoundException) {
return new NotFoundHttpException($exception->getMessage(), $exception);
}
return $exception;
}
private function getStatusCode($exception)
{
if (method_exists($exception, 'getStatusCode')) {
return $exception->getStatusCode();
}
$statusCode = $exception->getCode();
if (array_key_exists($statusCode, Response::$statusTexts)) {
return $statusCode;
}
return Response::HTTP_INTERNAL_SERVER_ERROR;
}
protected function getServiceKernel()
{
return ServiceKernel::instance();
}
}