vendor/symfony/http-kernel/EventListener/ExceptionListener.php line 79

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\HttpKernel\EventListener;
  11. use Psr\Log\LoggerInterface;
  12. use Symfony\Component\Debug\Exception\FlattenException;
  13. use Symfony\Component\Debug\ExceptionHandler;
  14. use Symfony\Component\EventDispatcher\EventDispatcherInterface;
  15. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  16. use Symfony\Component\HttpFoundation\Request;
  17. use Symfony\Component\HttpFoundation\Response;
  18. use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
  19. use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
  20. use Symfony\Component\HttpKernel\HttpKernelInterface;
  21. use Symfony\Component\HttpKernel\KernelEvents;
  22. use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
  23. /**
  24.  * @author Fabien Potencier <fabien@symfony.com>
  25.  *
  26.  * @final since Symfony 4.3
  27.  */
  28. class ExceptionListener implements EventSubscriberInterface
  29. {
  30.     protected $controller;
  31.     protected $logger;
  32.     protected $debug;
  33.     private $charset;
  34.     private $fileLinkFormat;
  35.     private $isTerminating false;
  36.     public function __construct($controllerLoggerInterface $logger null$debug falsestring $charset null$fileLinkFormat null)
  37.     {
  38.         $this->controller $controller;
  39.         $this->logger $logger;
  40.         $this->debug $debug;
  41.         $this->charset $charset;
  42.         $this->fileLinkFormat $fileLinkFormat;
  43.     }
  44.     public function logKernelException(GetResponseForExceptionEvent $event)
  45.     {
  46.         $e FlattenException::create($event->getException());
  47.         $this->logException($event->getException(), sprintf('Uncaught PHP Exception %s: "%s" at %s line %s'$e->getClass(), $e->getMessage(), $e->getFile(), $e->getLine()));
  48.     }
  49.     /**
  50.      * @param string                   $eventName
  51.      * @param EventDispatcherInterface $eventDispatcher
  52.      */
  53.     public function onKernelException(GetResponseForExceptionEvent $event)
  54.     {
  55.         if (null === $this->controller) {
  56.             if (!$event->isMasterRequest()) {
  57.                 return;
  58.             }
  59.             if (!$this->isTerminating) {
  60.                 $this->isTerminating true;
  61.                 return;
  62.             }
  63.             $this->isTerminating false;
  64.         }
  65.         $exception $event->getException();
  66.         $request $this->duplicateRequest($exception$event->getRequest());
  67.         $eventDispatcher = \func_num_args() > func_get_arg(2) : null;
  68.         try {
  69.             $response $event->getKernel()->handle($requestHttpKernelInterface::SUB_REQUESTfalse);
  70.         } catch (\Exception $e) {
  71.             $f FlattenException::create($e);
  72.             $this->logException($esprintf('Exception thrown when handling an exception (%s: %s at %s line %s)'$f->getClass(), $f->getMessage(), $e->getFile(), $e->getLine()));
  73.             $prev $e;
  74.             do {
  75.                 if ($exception === $wrapper $prev) {
  76.                     throw $e;
  77.                 }
  78.             } while ($prev $wrapper->getPrevious());
  79.             $prev = new \ReflectionProperty($wrapper instanceof \Exception ? \Exception::class : \Error::class, 'previous');
  80.             $prev->setAccessible(true);
  81.             $prev->setValue($wrapper$exception);
  82.             throw $e;
  83.         }
  84.         $event->setResponse($response);
  85.         if ($this->debug && $eventDispatcher instanceof EventDispatcherInterface) {
  86.             $cspRemovalListener = function ($event) use (&$cspRemovalListener$eventDispatcher) {
  87.                 $event->getResponse()->headers->remove('Content-Security-Policy');
  88.                 $eventDispatcher->removeListener(KernelEvents::RESPONSE$cspRemovalListener);
  89.             };
  90.             $eventDispatcher->addListener(KernelEvents::RESPONSE$cspRemovalListener, -128);
  91.         }
  92.     }
  93.     public function reset()
  94.     {
  95.         $this->isTerminating false;
  96.     }
  97.     public static function getSubscribedEvents()
  98.     {
  99.         return [
  100.             KernelEvents::EXCEPTION => [
  101.                 ['logKernelException'0],
  102.                 ['onKernelException', -128],
  103.             ],
  104.         ];
  105.     }
  106.     /**
  107.      * Logs an exception.
  108.      *
  109.      * @param \Exception $exception The \Exception instance
  110.      * @param string     $message   The error message to log
  111.      */
  112.     protected function logException(\Exception $exception$message)
  113.     {
  114.         if (null !== $this->logger) {
  115.             if (!$exception instanceof HttpExceptionInterface || $exception->getStatusCode() >= 500) {
  116.                 $this->logger->critical($message, ['exception' => $exception]);
  117.             } else {
  118.                 $this->logger->error($message, ['exception' => $exception]);
  119.             }
  120.         }
  121.     }
  122.     /**
  123.      * Clones the request for the exception.
  124.      *
  125.      * @param \Exception $exception The thrown exception
  126.      * @param Request    $request   The original request
  127.      *
  128.      * @return Request The cloned request
  129.      */
  130.     protected function duplicateRequest(\Exception $exceptionRequest $request)
  131.     {
  132.         $attributes = [
  133.             'exception' => $exception FlattenException::create($exception),
  134.             '_controller' => $this->controller ?: function () use ($exception) {
  135.                 $handler = new ExceptionHandler($this->debug$this->charset$this->fileLinkFormat);
  136.                 return new Response($handler->getHtml($exception), $exception->getStatusCode(), $exception->getHeaders());
  137.             },
  138.             'logger' => $this->logger instanceof DebugLoggerInterface $this->logger null,
  139.         ];
  140.         $request $request->duplicate(nullnull$attributes);
  141.         $request->setMethod('GET');
  142.         return $request;
  143.     }
  144. }