vendor/pimcore/pimcore/bundles/CoreBundle/EventListener/ResponseExceptionListener.php line 69

Open in your IDE?
  1. <?php
  2. /**
  3.  * Pimcore
  4.  *
  5.  * This source file is available under two different licenses:
  6.  * - GNU General Public License version 3 (GPLv3)
  7.  * - Pimcore Commercial License (PCL)
  8.  * Full copyright and license information is available in
  9.  * LICENSE.md which is distributed with this source code.
  10.  *
  11.  *  @copyright  Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12.  *  @license    http://www.pimcore.org/license     GPLv3 and PCL
  13.  */
  14. namespace Pimcore\Bundle\CoreBundle\EventListener;
  15. use Pimcore\Bundle\CoreBundle\EventListener\Traits\PimcoreContextAwareTrait;
  16. use Pimcore\Config;
  17. use Pimcore\Db\ConnectionInterface;
  18. use Pimcore\Document\Renderer\DocumentRenderer;
  19. use Pimcore\Http\Exception\ResponseException;
  20. use Pimcore\Http\Request\Resolver\PimcoreContextResolver;
  21. use Pimcore\Http\Request\Resolver\SiteResolver;
  22. use Pimcore\Model\Document;
  23. use Pimcore\Model\Site;
  24. use Psr\Log\LoggerAwareTrait;
  25. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  26. use Symfony\Component\HttpFoundation\Request;
  27. use Symfony\Component\HttpFoundation\Response;
  28. use Symfony\Component\HttpKernel\Event\ExceptionEvent;
  29. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  30. use Symfony\Component\HttpKernel\KernelEvents;
  31. /**
  32.  * @internal
  33.  */
  34. class ResponseExceptionListener implements EventSubscriberInterface
  35. {
  36.     use LoggerAwareTrait;
  37.     use PimcoreContextAwareTrait;
  38.     /**
  39.      * @param DocumentRenderer $documentRenderer
  40.      * @param ConnectionInterface $db
  41.      * @param Config $config
  42.      * @param Document\Service $documentService
  43.      * @param SiteResolver $siteResolver
  44.      */
  45.     public function __construct(
  46.         protected DocumentRenderer $documentRenderer,
  47.         protected ConnectionInterface $db,
  48.         protected Config $config,
  49.         protected Document\Service $documentService,
  50.         protected SiteResolver $siteResolver
  51.     ) {
  52.     }
  53.     /**
  54.      * {@inheritdoc}
  55.      */
  56.     public static function getSubscribedEvents()
  57.     {
  58.         return [
  59.             KernelEvents::EXCEPTION => 'onKernelException',
  60.         ];
  61.     }
  62.     public function onKernelException(ExceptionEvent $event)
  63.     {
  64.         $exception $event->getThrowable();
  65.         // handle ResponseException (can be used from any context)
  66.         if ($exception instanceof ResponseException) {
  67.             $event->setResponse($exception->getResponse());
  68.             // a response was explicitely set -> do not continue to error page
  69.             return;
  70.         }
  71.         // further checks are only valid for default context
  72.         $request $event->getRequest();
  73.         if ($this->matchesPimcoreContext($requestPimcoreContextResolver::CONTEXT_DEFAULT)) {
  74.             $this->handleErrorPage($event);
  75.         }
  76.     }
  77.     protected function handleErrorPage(ExceptionEvent $event)
  78.     {
  79.         if (\Pimcore::inDebugMode()) {
  80.             return;
  81.         }
  82.         $request $event->getRequest();
  83.         $exception $event->getThrowable();
  84.         $statusCode 500;
  85.         $headers = [];
  86.         if ($exception instanceof HttpExceptionInterface) {
  87.             $statusCode $exception->getStatusCode();
  88.             $headers $exception->getHeaders();
  89.         } else {
  90.             // only log exception if it's not intentional (like a NotFoundHttpException)
  91.             $this->logger->error($exception);
  92.         }
  93.         $errorPath $this->determineErrorPath($request);
  94.         $this->logToHttpErrorLog($event->getRequest(), $statusCode);
  95.         // Error page rendering
  96.         if (empty($errorPath)) {
  97.             // if not set, use Symfony error handling
  98.             return;
  99.         }
  100.         $document Document::getByPath($errorPath);
  101.         if (!$document instanceof Document\Page) {
  102.             // default is home
  103.             $document Document::getById(1);
  104.         }
  105.         try {
  106.             $response $this->documentRenderer->render($document, [
  107.                 'exception' => $exception,
  108.                 PimcoreContextListener::ATTRIBUTE_PIMCORE_CONTEXT_FORCE_RESOLVING => true,
  109.             ]);
  110.         } catch (\Exception $e) {
  111.             // we are even not able to render the error page, so we send the client a unicorn
  112.             $response 'Page not found. ðŸ¦„';
  113.             $this->logger->emergency('Unable to render error page, exception thrown');
  114.             $this->logger->emergency($e);
  115.         }
  116.         $event->setResponse(new Response($response$statusCode$headers));
  117.     }
  118.     protected function logToHttpErrorLog(Request $request$statusCode)
  119.     {
  120.         $uri $request->getUri();
  121.         $exists $this->db->fetchOne('SELECT date FROM http_error_log WHERE uri = ?'$uri);
  122.         if ($exists) {
  123.             $this->db->query('UPDATE http_error_log SET `count` = `count` + 1, date = ? WHERE uri = ?', [time(), $uri]);
  124.         } else {
  125.             $this->db->insert('http_error_log', [
  126.                 'uri' => $uri,
  127.                 'code' => (int) $statusCode,
  128.                 'parametersGet' => serialize($_GET),
  129.                 'parametersPost' => serialize($_POST),
  130.                 'cookies' => serialize($_COOKIE),
  131.                 'serverVars' => serialize($_SERVER),
  132.                 'date' => time(),
  133.                 'count' => 1,
  134.             ]);
  135.         }
  136.     }
  137.     /**
  138.      * @param Request $request
  139.      *
  140.      * @return string
  141.      *
  142.      * @throws \Exception
  143.      */
  144.     private function determineErrorPath(Request $request): string
  145.     {
  146.         $errorPath '';
  147.         if ($this->siteResolver->isSiteRequest($request)) {
  148.             $path $this->siteResolver->getSitePath($request);
  149.         } else {
  150.             $path urldecode($request->getPathInfo());
  151.         }
  152.         // Find nearest document by path
  153.         $document $this->documentService->getNearestDocumentByPath(
  154.             $path,
  155.             false,
  156.             ['page''snippet''hardlink''link''folder']
  157.         );
  158.         if ($document && $document->getFullPath() !== '/') {
  159.             if ($document->getProperty('language')) {
  160.                 $locale $document->getProperty('language');
  161.             }
  162.         }
  163.         if (Site::isSiteRequest()) {
  164.             $site Site::getCurrentSite();
  165.             $localizedErrorDocumentsPaths $site->getLocalizedErrorDocuments() ?: [];
  166.             $defaultErrorDocumentPath $site->getErrorDocument();
  167.         } else {
  168.             $localizedErrorDocumentsPaths $this->config['documents']['error_pages']['localized'] ?: [];
  169.             $defaultErrorDocumentPath $this->config['documents']['error_pages']['default'];
  170.         }
  171.         if (!empty($locale) && array_key_exists($locale$localizedErrorDocumentsPaths)) {
  172.             $errorPath $localizedErrorDocumentsPaths[$locale];
  173.         } else {
  174.             // If locale can't be determined check if error page is defined for any of user-agent preferences
  175.             foreach ($request->getLanguages() as $requestLocale) {
  176.                 if (!empty($localizedErrorDocumentsPaths[$requestLocale])) {
  177.                     $errorPath $this->config['documents']['error_pages']['localized'][$requestLocale];
  178.                     break;
  179.                 }
  180.             }
  181.         }
  182.         if (empty($errorPath)) {
  183.             $errorPath $defaultErrorDocumentPath;
  184.         }
  185.         return $errorPath;
  186.     }
  187. }