vendor/pimcore/pimcore/bundles/AdminBundle/EventListener/AdminExceptionListener.php line 50

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4.  * Pimcore
  5.  *
  6.  * This source file is available under two different licenses:
  7.  * - GNU General Public License version 3 (GPLv3)
  8.  * - Pimcore Enterprise License (PEL)
  9.  * Full copyright and license information is available in
  10.  * LICENSE.md which is distributed with this source code.
  11.  *
  12.  * @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  13.  * @license    http://www.pimcore.org/license     GPLv3 and PEL
  14.  */
  15. namespace Pimcore\Bundle\AdminBundle\EventListener;
  16. use Doctrine\DBAL\DBALException;
  17. use Pimcore\Bundle\AdminBundle\HttpFoundation\JsonResponse;
  18. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  19. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  20. use Pimcore\Model\Element\ValidationException;
  21. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  22. use Symfony\Component\HttpFoundation\Response;
  23. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  24. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  25. use Symfony\Component\HttpKernel\KernelEvents;
  26. class AdminExceptionListener implements EventSubscriberInterface
  27. {
  28.     use PimcoreContextAwareTrait;
  29.     /**
  30.      * @inheritDoc
  31.      */
  32.     public static function getSubscribedEvents(): array
  33.     {
  34.         return [
  35.             KernelEvents::EXCEPTION => 'onKernelException'
  36.         ];
  37.     }
  38.     /**
  39.      * Return JSON error responses from webservice context
  40.      *
  41.      * @param GetResponseForExceptionEvent $event
  42.      */
  43.     public function onKernelException(GetResponseForExceptionEvent $event)
  44.     {
  45.         $request $event->getRequest();
  46.         $ex $event->getException();
  47.         if ($this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_ADMIN)) {
  48.             // only return JSON error for XHR requests
  49.             if (!$request->isXmlHttpRequest()) {
  50.                 return;
  51.             }
  52.             list($code$headers$message) = $this->getResponseData($ex);
  53.             if (!\Pimcore::inDebugMode()) {
  54.                 // DBAL exceptions do include SQL statements, we don't want to expose them
  55.                 if ($ex instanceof DBALException) {
  56.                     $message 'Database error, see logs for details';
  57.                 }
  58.             }
  59.             $data = [
  60.                 'success' => false,
  61.                 'message' => $message,
  62.             ];
  63.             if (\Pimcore::inDebugMode()) {
  64.                 $data['trace'] = $ex->getTrace();
  65.                 $data['traceString'] = 'in ' $ex->getFile() . ':' $ex->getLine() . "\n" $ex->getTraceAsString();
  66.             }
  67.             if ($ex instanceof ValidationException) {
  68.                 $data['type'] = 'ValidationException';
  69.                 $code 422;
  70.                 $this->recursiveAddValidationExceptionSubItems($ex->getSubItems(), $message$data['traceString']);
  71.             }
  72.             $response = new JsonResponse($data$code$headers);
  73.             $event->setResponse($response);
  74.             return;
  75.         } elseif ($this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_WEBSERVICE)) {
  76.             list($code$headers$message) = $this->getResponseData($ex);
  77.             if ($ex instanceof DBALException) {
  78.                 $message 'Database error, see logs for details';
  79.             }
  80.             $data = [
  81.                 'success' => false,
  82.                 'msg' => $message
  83.             ];
  84.             if (\Pimcore::inDebugMode()) {
  85.                 $data['trace'] = $ex->getTrace();
  86.                 $data['traceString'] = $ex->getTraceAsString();
  87.             }
  88.             $response = new JsonResponse($data$code$headers);
  89.             $event->setResponse($response);
  90.             return;
  91.         }
  92.     }
  93.     private function getResponseData(\Exception $exint $defaultStatusCode 500): array
  94.     {
  95.         $code $defaultStatusCode;
  96.         $headers = [];
  97.         $message $ex->getMessage();
  98.         if ($ex instanceof HttpExceptionInterface) {
  99.             if (empty($message)) {
  100.                 $message Response::$statusTexts[$ex->getStatusCode()];
  101.             }
  102.             $code $ex->getStatusCode();
  103.             $headers $ex->getHeaders();
  104.         }
  105.         return [$code$headers$message];
  106.     }
  107.     /**
  108.      * @param ValidationException[] $items
  109.      * @param string $message
  110.      * @param string $detailedInfo
  111.      */
  112.     protected function recursiveAddValidationExceptionSubItems($items, &$message, &$detailedInfo)
  113.     {
  114.         if (!$items) {
  115.             return;
  116.         }
  117.         foreach ($items as $e) {
  118.             if ($e->getMessage()) {
  119.                 $message .= '<b>' $e->getMessage() . '</b>';
  120.                 $this->addContext($e$message);
  121.                 $message .= '<br>';
  122.                 $detailedInfo .= '<br><b>Message:</b><br>';
  123.                 $detailedInfo .= $e->getMessage() . '<br>';
  124.                 $inner $this->getInnerStack($e);
  125.                 $detailedInfo .= '<br><b>Trace:</b> ' $inner->getTraceAsString() . '<br>';
  126.             }
  127.             $this->recursiveAddValidationExceptionSubItems($e->getSubItems(), $message$detailedInfo);
  128.         }
  129.     }
  130.     /**
  131.      * @param ValidationException $e
  132.      * @param string $message
  133.      */
  134.     protected function addContext(ValidationException $e, &$message)
  135.     {
  136.         $contextStack $e->getContextStack();
  137.         if ($contextStack) {
  138.             $message $message ' (' implode(','$contextStack) . ')';
  139.         }
  140.     }
  141.     /**
  142.      * @param \Exception $e
  143.      *
  144.      * @return \Exception
  145.      */
  146.     protected function getInnerStack(\Exception $e)
  147.     {
  148.         while ($e->getPrevious()) {
  149.             $e $e->getPrevious();
  150.         }
  151.         return $e;
  152.     }
  153. }