vendor/symfony/http-kernel/EventListener/DebugHandlersListener.php line 76

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\Console\ConsoleEvents;
  13. use Symfony\Component\Console\Event\ConsoleEvent;
  14. use Symfony\Component\Console\Output\ConsoleOutputInterface;
  15. use Symfony\Component\ErrorHandler\ErrorHandler;
  16. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  17. use Symfony\Component\HttpKernel\Event\KernelEvent;
  18. use Symfony\Component\HttpKernel\KernelEvents;
  19. /**
  20.  * Configures errors and exceptions handlers.
  21.  *
  22.  * @author Nicolas Grekas <p@tchwork.com>
  23.  *
  24.  * @final
  25.  *
  26.  * @internal since Symfony 5.3
  27.  */
  28. class DebugHandlersListener implements EventSubscriberInterface
  29. {
  30.     private $earlyHandler;
  31.     private $exceptionHandler;
  32.     private $logger;
  33.     private $deprecationLogger;
  34.     private $levels;
  35.     private $throwAt;
  36.     private $scream;
  37.     private $scope;
  38.     private $firstCall true;
  39.     private $hasTerminatedWithException;
  40.     /**
  41.      * @param callable|null  $exceptionHandler A handler that must support \Throwable instances that will be called on Exception
  42.      * @param array|int|null $levels           An array map of E_* to LogLevel::* or an integer bit field of E_* constants
  43.      * @param int|null       $throwAt          Thrown errors in a bit field of E_* constants, or null to keep the current value
  44.      * @param bool           $scream           Enables/disables screaming mode, where even silenced errors are logged
  45.      * @param bool           $scope            Enables/disables scoping mode
  46.      */
  47.     public function __construct(?callable $exceptionHandler null, ?LoggerInterface $logger null$levels \E_ALL, ?int $throwAt \E_ALLbool $scream true$scope true$deprecationLogger null$fileLinkFormat null)
  48.     {
  49.         if (!\is_bool($scope)) {
  50.             trigger_deprecation('symfony/http-kernel''5.4''Passing a $fileLinkFormat is deprecated.');
  51.             $scope $deprecationLogger;
  52.             $deprecationLogger $fileLinkFormat;
  53.         }
  54.         $handler set_exception_handler('is_int');
  55.         $this->earlyHandler \is_array($handler) ? $handler[0] : null;
  56.         restore_exception_handler();
  57.         $this->exceptionHandler $exceptionHandler;
  58.         $this->logger $logger;
  59.         $this->levels $levels ?? \E_ALL;
  60.         $this->throwAt \is_int($throwAt) ? $throwAt : (null === $throwAt null : ($throwAt \E_ALL null));
  61.         $this->scream $scream;
  62.         $this->scope $scope;
  63.         $this->deprecationLogger $deprecationLogger;
  64.     }
  65.     /**
  66.      * Configures the error handler.
  67.      */
  68.     public function configure(?object $event null)
  69.     {
  70.         if ($event instanceof ConsoleEvent && !\in_array(\PHP_SAPI, ['cli''phpdbg'], true)) {
  71.             return;
  72.         }
  73.         if (!$event instanceof KernelEvent ? !$this->firstCall : !$event->isMainRequest()) {
  74.             return;
  75.         }
  76.         $this->firstCall $this->hasTerminatedWithException false;
  77.         $hasRun null;
  78.         $handler set_exception_handler('is_int');
  79.         $handler \is_array($handler) ? $handler[0] : null;
  80.         restore_exception_handler();
  81.         if (!$handler instanceof ErrorHandler) {
  82.             $handler $this->earlyHandler;
  83.         }
  84.         if ($handler instanceof ErrorHandler) {
  85.             if ($this->logger || $this->deprecationLogger) {
  86.                 $this->setDefaultLoggers($handler);
  87.                 if (\is_array($this->levels)) {
  88.                     $levels 0;
  89.                     foreach ($this->levels as $type => $log) {
  90.                         $levels |= $type;
  91.                     }
  92.                 } else {
  93.                     $levels $this->levels;
  94.                 }
  95.                 if ($this->scream) {
  96.                     $handler->screamAt($levels);
  97.                 }
  98.                 if ($this->scope) {
  99.                     $handler->scopeAt($levels & ~\E_USER_DEPRECATED & ~\E_DEPRECATED);
  100.                 } else {
  101.                     $handler->scopeAt(0true);
  102.                 }
  103.                 $this->logger $this->deprecationLogger $this->levels null;
  104.             }
  105.             if (null !== $this->throwAt) {
  106.                 $handler->throwAt($this->throwAttrue);
  107.             }
  108.         }
  109.         if (!$this->exceptionHandler) {
  110.             if ($event instanceof KernelEvent) {
  111.                 if (method_exists($kernel $event->getKernel(), 'terminateWithException')) {
  112.                     $request $event->getRequest();
  113.                     $hasRun = &$this->hasTerminatedWithException;
  114.                     $this->exceptionHandler = static function (\Throwable $e) use ($kernel$request, &$hasRun) {
  115.                         if ($hasRun) {
  116.                             throw $e;
  117.                         }
  118.                         $hasRun true;
  119.                         $kernel->terminateWithException($e$request);
  120.                     };
  121.                 }
  122.             } elseif ($event instanceof ConsoleEvent && $app $event->getCommand()->getApplication()) {
  123.                 $output $event->getOutput();
  124.                 if ($output instanceof ConsoleOutputInterface) {
  125.                     $output $output->getErrorOutput();
  126.                 }
  127.                 $this->exceptionHandler = static function (\Throwable $e) use ($app$output) {
  128.                     $app->renderThrowable($e$output);
  129.                 };
  130.             }
  131.         }
  132.         if ($this->exceptionHandler) {
  133.             if ($handler instanceof ErrorHandler) {
  134.                 $handler->setExceptionHandler($this->exceptionHandler);
  135.                 if (null !== $hasRun) {
  136.                     $throwAt $handler->throwAt(0) | \E_ERROR \E_CORE_ERROR \E_COMPILE_ERROR \E_USER_ERROR \E_RECOVERABLE_ERROR \E_PARSE;
  137.                     $loggers = [];
  138.                     foreach ($handler->setLoggers([]) as $type => $log) {
  139.                         if ($type $throwAt) {
  140.                             $loggers[$type] = [null$log[1]];
  141.                         }
  142.                     }
  143.                     // Assume $kernel->terminateWithException() will log uncaught exceptions appropriately
  144.                     $handler->setLoggers($loggers);
  145.                 }
  146.             }
  147.             $this->exceptionHandler null;
  148.         }
  149.     }
  150.     private function setDefaultLoggers(ErrorHandler $handler): void
  151.     {
  152.         if (\is_array($this->levels)) {
  153.             $levelsDeprecatedOnly = [];
  154.             $levelsWithoutDeprecated = [];
  155.             foreach ($this->levels as $type => $log) {
  156.                 if (\E_DEPRECATED == $type || \E_USER_DEPRECATED == $type) {
  157.                     $levelsDeprecatedOnly[$type] = $log;
  158.                 } else {
  159.                     $levelsWithoutDeprecated[$type] = $log;
  160.                 }
  161.             }
  162.         } else {
  163.             $levelsDeprecatedOnly $this->levels & (\E_DEPRECATED \E_USER_DEPRECATED);
  164.             $levelsWithoutDeprecated $this->levels & ~\E_DEPRECATED & ~\E_USER_DEPRECATED;
  165.         }
  166.         $defaultLoggerLevels $this->levels;
  167.         if ($this->deprecationLogger && $levelsDeprecatedOnly) {
  168.             $handler->setDefaultLogger($this->deprecationLogger$levelsDeprecatedOnly);
  169.             $defaultLoggerLevels $levelsWithoutDeprecated;
  170.         }
  171.         if ($this->logger && $defaultLoggerLevels) {
  172.             $handler->setDefaultLogger($this->logger$defaultLoggerLevels);
  173.         }
  174.     }
  175.     public static function getSubscribedEvents(): array
  176.     {
  177.         $events = [KernelEvents::REQUEST => ['configure'2048]];
  178.         if (\defined('Symfony\Component\Console\ConsoleEvents::COMMAND')) {
  179.             $events[ConsoleEvents::COMMAND] = ['configure'2048];
  180.         }
  181.         return $events;
  182.     }
  183. }