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

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 Commercial License (PCL)
  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 PCL
  14.  */
  15. namespace Pimcore\Bundle\AdminBundle\EventListener;
  16. use Doctrine\DBAL\Exception as 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 Pimcore\Model\Exception\ConfigWriteException;
  22. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  23. use Symfony\Component\HttpFoundation\Response;
  24. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  25. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  26. use Symfony\Component\HttpKernel\KernelEvents;
  27. /**
  28.  * @internal
  29.  */
  30. class AdminExceptionListener implements EventSubscriberInterface
  31. {
  32.     use PimcoreContextAwareTrait;
  33.     /**
  34.      * {@inheritdoc}
  35.      */
  36.     public static function getSubscribedEvents(): array
  37.     {
  38.         return [
  39.             KernelEvents::EXCEPTION => 'onKernelException',
  40.         ];
  41.     }
  42.     /**
  43.      * @param ExceptionEvent $event
  44.      */
  45.     public function onKernelException(ExceptionEvent $event)
  46.     {
  47.         $request $event->getRequest();
  48.         $ex $event->getThrowable();
  49.         if ($this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_ADMIN)) {
  50.             // only return JSON error for XHR requests
  51.             if (!$request->isXmlHttpRequest()) {
  52.                 return;
  53.             }
  54.             list($code$headers$message) = $this->getResponseData($ex);
  55.             $data = [
  56.                 'success' => false,
  57.             ];
  58.             if (!\Pimcore::inDebugMode()) {
  59.                 // DBAL exceptions do include SQL statements, we don't want to expose them
  60.                 if ($ex instanceof DBALException) {
  61.                     $message 'Database error, see logs for details';
  62.                 }
  63.             }
  64.             if (\Pimcore::inDebugMode()) {
  65.                 $data['trace'] = $ex->getTrace();
  66.                 $data['traceString'] = 'in ' $ex->getFile() . ':' $ex->getLine() . "\n" $ex->getTraceAsString();
  67.             }
  68.             if ($ex instanceof ValidationException) {
  69.                 $data['type'] = 'ValidationException';
  70.                 $code 422;
  71.                 $this->recursiveAddValidationExceptionSubItems($ex->getSubItems(), $message$data['traceString']);
  72.             }
  73.             if ($ex instanceof ConfigWriteException) {
  74.                 $data['type'] = 'ConfigWriteException';
  75.                 $code 422;
  76.             }
  77.             $data['message'] = $message;
  78.             $response = new JsonResponse($data$code$headers);
  79.             $event->setResponse($response);
  80.             return;
  81.         }
  82.     }
  83.     private function getResponseData(\Throwable $exint $defaultStatusCode 500): array
  84.     {
  85.         $code $defaultStatusCode;
  86.         $headers = [];
  87.         $message $ex->getMessage();
  88.         if ($ex instanceof HttpExceptionInterface) {
  89.             if (empty($message)) {
  90.                 $message Response::$statusTexts[$ex->getStatusCode()];
  91.             }
  92.             $code $ex->getStatusCode();
  93.             $headers $ex->getHeaders();
  94.         }
  95.         return [$code$headers$message];
  96.     }
  97.     /**
  98.      * @param ValidationException[] $items
  99.      * @param string $message
  100.      * @param string $detailedInfo
  101.      */
  102.     protected function recursiveAddValidationExceptionSubItems($items, &$message, &$detailedInfo)
  103.     {
  104.         if (!$items) {
  105.             return;
  106.         }
  107.         foreach ($items as $e) {
  108.             if ($e->getMessage()) {
  109.                 $message .= '<b>' $e->getMessage() . '</b>';
  110.                 $this->addContext($e$message);
  111.                 $message .= '<br>';
  112.                 $detailedInfo .= '<br><b>Message:</b><br>';
  113.                 $detailedInfo .= $e->getMessage() . '<br>';
  114.                 $inner $this->getInnerStack($e);
  115.                 $detailedInfo .= '<br><b>Trace:</b> ' $inner->getTraceAsString() . '<br>';
  116.             }
  117.             $this->recursiveAddValidationExceptionSubItems($e->getSubItems(), $message$detailedInfo);
  118.         }
  119.     }
  120.     /**
  121.      * @param ValidationException $e
  122.      * @param string $message
  123.      */
  124.     protected function addContext(ValidationException $e, &$message)
  125.     {
  126.         $contextStack $e->getContextStack();
  127.         if ($contextStack) {
  128.             $message $message ' (' implode(','$contextStack) . ')';
  129.         }
  130.     }
  131.     /**
  132.      * @param \Throwable $e
  133.      *
  134.      * @return \Throwable
  135.      */
  136.     protected function getInnerStack(\Throwable $e)
  137.     {
  138.         while ($e->getPrevious()) {
  139.             $e $e->getPrevious();
  140.         }
  141.         return $e;
  142.     }
  143. }