[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/vendor/symfony/error-handler/ -> ErrorHandler.php (source)

   1  <?php
   2  
   3  /*
   4   * This file is part of the Symfony package.
   5   *
   6   * (c) Fabien Potencier <[email protected]>
   7   *
   8   * For the full copyright and license information, please view the LICENSE
   9   * file that was distributed with this source code.
  10   */
  11  
  12  namespace Symfony\Component\ErrorHandler;
  13  
  14  use Psr\Log\LoggerInterface;
  15  use Psr\Log\LogLevel;
  16  use Symfony\Component\ErrorHandler\Error\FatalError;
  17  use Symfony\Component\ErrorHandler\Error\OutOfMemoryError;
  18  use Symfony\Component\ErrorHandler\ErrorEnhancer\ClassNotFoundErrorEnhancer;
  19  use Symfony\Component\ErrorHandler\ErrorEnhancer\ErrorEnhancerInterface;
  20  use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedFunctionErrorEnhancer;
  21  use Symfony\Component\ErrorHandler\ErrorEnhancer\UndefinedMethodErrorEnhancer;
  22  use Symfony\Component\ErrorHandler\ErrorRenderer\CliErrorRenderer;
  23  use Symfony\Component\ErrorHandler\ErrorRenderer\HtmlErrorRenderer;
  24  use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext;
  25  
  26  /**
  27   * A generic ErrorHandler for the PHP engine.
  28   *
  29   * Provides five bit fields that control how errors are handled:
  30   * - thrownErrors: errors thrown as \ErrorException
  31   * - loggedErrors: logged errors, when not @-silenced
  32   * - scopedErrors: errors thrown or logged with their local context
  33   * - tracedErrors: errors logged with their stack trace
  34   * - screamedErrors: never @-silenced errors
  35   *
  36   * Each error level can be logged by a dedicated PSR-3 logger object.
  37   * Screaming only applies to logging.
  38   * Throwing takes precedence over logging.
  39   * Uncaught exceptions are logged as E_ERROR.
  40   * E_DEPRECATED and E_USER_DEPRECATED levels never throw.
  41   * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw.
  42   * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so.
  43   * As errors have a performance cost, repeated errors are all logged, so that the developer
  44   * can see them and weight them as more important to fix than others of the same level.
  45   *
  46   * @author Nicolas Grekas <[email protected]>
  47   * @author GrĂ©goire Pineau <[email protected]>
  48   *
  49   * @final
  50   */
  51  class ErrorHandler
  52  {
  53      private $levels = [
  54          \E_DEPRECATED => 'Deprecated',
  55          \E_USER_DEPRECATED => 'User Deprecated',
  56          \E_NOTICE => 'Notice',
  57          \E_USER_NOTICE => 'User Notice',
  58          \E_STRICT => 'Runtime Notice',
  59          \E_WARNING => 'Warning',
  60          \E_USER_WARNING => 'User Warning',
  61          \E_COMPILE_WARNING => 'Compile Warning',
  62          \E_CORE_WARNING => 'Core Warning',
  63          \E_USER_ERROR => 'User Error',
  64          \E_RECOVERABLE_ERROR => 'Catchable Fatal Error',
  65          \E_COMPILE_ERROR => 'Compile Error',
  66          \E_PARSE => 'Parse Error',
  67          \E_ERROR => 'Error',
  68          \E_CORE_ERROR => 'Core Error',
  69      ];
  70  
  71      private $loggers = [
  72          \E_DEPRECATED => [null, LogLevel::INFO],
  73          \E_USER_DEPRECATED => [null, LogLevel::INFO],
  74          \E_NOTICE => [null, LogLevel::WARNING],
  75          \E_USER_NOTICE => [null, LogLevel::WARNING],
  76          \E_STRICT => [null, LogLevel::WARNING],
  77          \E_WARNING => [null, LogLevel::WARNING],
  78          \E_USER_WARNING => [null, LogLevel::WARNING],
  79          \E_COMPILE_WARNING => [null, LogLevel::WARNING],
  80          \E_CORE_WARNING => [null, LogLevel::WARNING],
  81          \E_USER_ERROR => [null, LogLevel::CRITICAL],
  82          \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
  83          \E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
  84          \E_PARSE => [null, LogLevel::CRITICAL],
  85          \E_ERROR => [null, LogLevel::CRITICAL],
  86          \E_CORE_ERROR => [null, LogLevel::CRITICAL],
  87      ];
  88  
  89      private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
  90      private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
  91      private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE
  92      private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE
  93      private $loggedErrors = 0;
  94      private $configureException;
  95      private $debug;
  96  
  97      private $isRecursive = 0;
  98      private $isRoot = false;
  99      private $exceptionHandler;
 100      private $bootstrappingLogger;
 101  
 102      private static $reservedMemory;
 103      private static $toStringException;
 104      private static $silencedErrorCache = [];
 105      private static $silencedErrorCount = 0;
 106      private static $exitCode = 0;
 107  
 108      /**
 109       * Registers the error handler.
 110       */
 111      public static function register(self $handler = null, bool $replace = true): self
 112      {
 113          if (null === self::$reservedMemory) {
 114              self::$reservedMemory = str_repeat('x', 32768);
 115              register_shutdown_function(__CLASS__.'::handleFatalError');
 116          }
 117  
 118          if ($handlerIsNew = null === $handler) {
 119              $handler = new static();
 120          }
 121  
 122          if (null === $prev = set_error_handler([$handler, 'handleError'])) {
 123              restore_error_handler();
 124              // Specifying the error types earlier would expose us to https://bugs.php.net/63206
 125              set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors);
 126              $handler->isRoot = true;
 127          }
 128  
 129          if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) {
 130              $handler = $prev[0];
 131              $replace = false;
 132          }
 133          if (!$replace && $prev) {
 134              restore_error_handler();
 135              $handlerIsRegistered = \is_array($prev) && $handler === $prev[0];
 136          } else {
 137              $handlerIsRegistered = true;
 138          }
 139          if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) {
 140              restore_exception_handler();
 141              if (!$handlerIsRegistered) {
 142                  $handler = $prev[0];
 143              } elseif ($handler !== $prev[0] && $replace) {
 144                  set_exception_handler([$handler, 'handleException']);
 145                  $p = $prev[0]->setExceptionHandler(null);
 146                  $handler->setExceptionHandler($p);
 147                  $prev[0]->setExceptionHandler($p);
 148              }
 149          } else {
 150              $handler->setExceptionHandler($prev ?? [$handler, 'renderException']);
 151          }
 152  
 153          $handler->throwAt(\E_ALL & $handler->thrownErrors, true);
 154  
 155          return $handler;
 156      }
 157  
 158      /**
 159       * Calls a function and turns any PHP error into \ErrorException.
 160       *
 161       * @return mixed What $function(...$arguments) returns
 162       *
 163       * @throws \ErrorException When $function(...$arguments) triggers a PHP error
 164       */
 165      public static function call(callable $function, ...$arguments)
 166      {
 167          set_error_handler(static function (int $type, string $message, string $file, int $line) {
 168              if (__FILE__ === $file) {
 169                  $trace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3);
 170                  $file = $trace[2]['file'] ?? $file;
 171                  $line = $trace[2]['line'] ?? $line;
 172              }
 173  
 174              throw new \ErrorException($message, 0, $type, $file, $line);
 175          });
 176  
 177          try {
 178              return $function(...$arguments);
 179          } finally {
 180              restore_error_handler();
 181          }
 182      }
 183  
 184      public function __construct(BufferingLogger $bootstrappingLogger = null, bool $debug = false)
 185      {
 186          if ($bootstrappingLogger) {
 187              $this->bootstrappingLogger = $bootstrappingLogger;
 188              $this->setDefaultLogger($bootstrappingLogger);
 189          }
 190          $traceReflector = new \ReflectionProperty(\Exception::class, 'trace');
 191          $traceReflector->setAccessible(true);
 192          $this->configureException = \Closure::bind(static function ($e, $trace, $file = null, $line = null) use ($traceReflector) {
 193              $traceReflector->setValue($e, $trace);
 194              $e->file = $file ?? $e->file;
 195              $e->line = $line ?? $e->line;
 196          }, null, new class() extends \Exception {
 197          });
 198          $this->debug = $debug;
 199      }
 200  
 201      /**
 202       * Sets a logger to non assigned errors levels.
 203       *
 204       * @param LoggerInterface $logger  A PSR-3 logger to put as default for the given levels
 205       * @param array|int|null  $levels  An array map of E_* to LogLevel::* or an integer bit field of E_* constants
 206       * @param bool            $replace Whether to replace or not any existing logger
 207       */
 208      public function setDefaultLogger(LoggerInterface $logger, $levels = \E_ALL, bool $replace = false): void
 209      {
 210          $loggers = [];
 211  
 212          if (\is_array($levels)) {
 213              foreach ($levels as $type => $logLevel) {
 214                  if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
 215                      $loggers[$type] = [$logger, $logLevel];
 216                  }
 217              }
 218          } else {
 219              if (null === $levels) {
 220                  $levels = \E_ALL;
 221              }
 222              foreach ($this->loggers as $type => $log) {
 223                  if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
 224                      $log[0] = $logger;
 225                      $loggers[$type] = $log;
 226                  }
 227              }
 228          }
 229  
 230          $this->setLoggers($loggers);
 231      }
 232  
 233      /**
 234       * Sets a logger for each error level.
 235       *
 236       * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
 237       *
 238       * @return array The previous map
 239       *
 240       * @throws \InvalidArgumentException
 241       */
 242      public function setLoggers(array $loggers): array
 243      {
 244          $prevLogged = $this->loggedErrors;
 245          $prev = $this->loggers;
 246          $flush = [];
 247  
 248          foreach ($loggers as $type => $log) {
 249              if (!isset($prev[$type])) {
 250                  throw new \InvalidArgumentException('Unknown error type: '.$type);
 251              }
 252              if (!\is_array($log)) {
 253                  $log = [$log];
 254              } elseif (!\array_key_exists(0, $log)) {
 255                  throw new \InvalidArgumentException('No logger provided.');
 256              }
 257              if (null === $log[0]) {
 258                  $this->loggedErrors &= ~$type;
 259              } elseif ($log[0] instanceof LoggerInterface) {
 260                  $this->loggedErrors |= $type;
 261              } else {
 262                  throw new \InvalidArgumentException('Invalid logger provided.');
 263              }
 264              $this->loggers[$type] = $log + $prev[$type];
 265  
 266              if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
 267                  $flush[$type] = $type;
 268              }
 269          }
 270          $this->reRegister($prevLogged | $this->thrownErrors);
 271  
 272          if ($flush) {
 273              foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
 274                  $type = ThrowableUtils::getSeverity($log[2]['exception']);
 275                  if (!isset($flush[$type])) {
 276                      $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
 277                  } elseif ($this->loggers[$type][0]) {
 278                      $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
 279                  }
 280              }
 281          }
 282  
 283          return $prev;
 284      }
 285  
 286      /**
 287       * Sets a user exception handler.
 288       *
 289       * @param callable(\Throwable $e)|null $handler
 290       *
 291       * @return callable|null The previous exception handler
 292       */
 293      public function setExceptionHandler(?callable $handler): ?callable
 294      {
 295          $prev = $this->exceptionHandler;
 296          $this->exceptionHandler = $handler;
 297  
 298          return $prev;
 299      }
 300  
 301      /**
 302       * Sets the PHP error levels that throw an exception when a PHP error occurs.
 303       *
 304       * @param int  $levels  A bit field of E_* constants for thrown errors
 305       * @param bool $replace Replace or amend the previous value
 306       *
 307       * @return int The previous value
 308       */
 309      public function throwAt(int $levels, bool $replace = false): int
 310      {
 311          $prev = $this->thrownErrors;
 312          $this->thrownErrors = ($levels | \E_RECOVERABLE_ERROR | \E_USER_ERROR) & ~\E_USER_DEPRECATED & ~\E_DEPRECATED;
 313          if (!$replace) {
 314              $this->thrownErrors |= $prev;
 315          }
 316          $this->reRegister($prev | $this->loggedErrors);
 317  
 318          return $prev;
 319      }
 320  
 321      /**
 322       * Sets the PHP error levels for which local variables are preserved.
 323       *
 324       * @param int  $levels  A bit field of E_* constants for scoped errors
 325       * @param bool $replace Replace or amend the previous value
 326       *
 327       * @return int The previous value
 328       */
 329      public function scopeAt(int $levels, bool $replace = false): int
 330      {
 331          $prev = $this->scopedErrors;
 332          $this->scopedErrors = $levels;
 333          if (!$replace) {
 334              $this->scopedErrors |= $prev;
 335          }
 336  
 337          return $prev;
 338      }
 339  
 340      /**
 341       * Sets the PHP error levels for which the stack trace is preserved.
 342       *
 343       * @param int  $levels  A bit field of E_* constants for traced errors
 344       * @param bool $replace Replace or amend the previous value
 345       *
 346       * @return int The previous value
 347       */
 348      public function traceAt(int $levels, bool $replace = false): int
 349      {
 350          $prev = $this->tracedErrors;
 351          $this->tracedErrors = $levels;
 352          if (!$replace) {
 353              $this->tracedErrors |= $prev;
 354          }
 355  
 356          return $prev;
 357      }
 358  
 359      /**
 360       * Sets the error levels where the @-operator is ignored.
 361       *
 362       * @param int  $levels  A bit field of E_* constants for screamed errors
 363       * @param bool $replace Replace or amend the previous value
 364       *
 365       * @return int The previous value
 366       */
 367      public function screamAt(int $levels, bool $replace = false): int
 368      {
 369          $prev = $this->screamedErrors;
 370          $this->screamedErrors = $levels;
 371          if (!$replace) {
 372              $this->screamedErrors |= $prev;
 373          }
 374  
 375          return $prev;
 376      }
 377  
 378      /**
 379       * Re-registers as a PHP error handler if levels changed.
 380       */
 381      private function reRegister(int $prev): void
 382      {
 383          if ($prev !== $this->thrownErrors | $this->loggedErrors) {
 384              $handler = set_error_handler('var_dump');
 385              $handler = \is_array($handler) ? $handler[0] : null;
 386              restore_error_handler();
 387              if ($handler === $this) {
 388                  restore_error_handler();
 389                  if ($this->isRoot) {
 390                      set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors);
 391                  } else {
 392                      set_error_handler([$this, 'handleError']);
 393                  }
 394              }
 395          }
 396      }
 397  
 398      /**
 399       * Handles errors by filtering then logging them according to the configured bit fields.
 400       *
 401       * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
 402       *
 403       * @throws \ErrorException When $this->thrownErrors requests so
 404       *
 405       * @internal
 406       */
 407      public function handleError(int $type, string $message, string $file, int $line): bool
 408      {
 409          if (\PHP_VERSION_ID >= 70300 && \E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) {
 410              $type = \E_DEPRECATED;
 411          }
 412  
 413          // Level is the current error reporting level to manage silent error.
 414          $level = error_reporting();
 415          $silenced = 0 === ($level & $type);
 416          // Strong errors are not authorized to be silenced.
 417          $level |= \E_RECOVERABLE_ERROR | \E_USER_ERROR | \E_DEPRECATED | \E_USER_DEPRECATED;
 418          $log = $this->loggedErrors & $type;
 419          $throw = $this->thrownErrors & $type & $level;
 420          $type &= $level | $this->screamedErrors;
 421  
 422          // Never throw on warnings triggered by assert()
 423          if (\E_WARNING === $type && 'a' === $message[0] && 0 === strncmp($message, 'assert(): ', 10)) {
 424              $throw = 0;
 425          }
 426  
 427          if (!$type || (!$log && !$throw)) {
 428              return false;
 429          }
 430  
 431          $logMessage = $this->levels[$type].': '.$message;
 432  
 433          if (null !== self::$toStringException) {
 434              $errorAsException = self::$toStringException;
 435              self::$toStringException = null;
 436          } elseif (!$throw && !($type & $level)) {
 437              if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
 438                  $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 5), $type, $file, $line, false) : [];
 439                  $errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace);
 440              } elseif (isset(self::$silencedErrorCache[$id][$message])) {
 441                  $lightTrace = null;
 442                  $errorAsException = self::$silencedErrorCache[$id][$message];
 443                  ++$errorAsException->count;
 444              } else {
 445                  $lightTrace = [];
 446                  $errorAsException = null;
 447              }
 448  
 449              if (100 < ++self::$silencedErrorCount) {
 450                  self::$silencedErrorCache = $lightTrace = [];
 451                  self::$silencedErrorCount = 1;
 452              }
 453              if ($errorAsException) {
 454                  self::$silencedErrorCache[$id][$message] = $errorAsException;
 455              }
 456              if (null === $lightTrace) {
 457                  return true;
 458              }
 459          } else {
 460              if (false !== strpos($message, '@anonymous')) {
 461                  $backtrace = debug_backtrace(false, 5);
 462  
 463                  for ($i = 1; isset($backtrace[$i]); ++$i) {
 464                      if (isset($backtrace[$i]['function'], $backtrace[$i]['args'][0])
 465                          && ('trigger_error' === $backtrace[$i]['function'] || 'user_error' === $backtrace[$i]['function'])
 466                      ) {
 467                          if ($backtrace[$i]['args'][0] !== $message) {
 468                              $message = $this->parseAnonymousClass($backtrace[$i]['args'][0]);
 469                              $logMessage = $this->levels[$type].': '.$message;
 470                          }
 471  
 472                          break;
 473                      }
 474                  }
 475              }
 476  
 477              $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
 478  
 479              if ($throw || $this->tracedErrors & $type) {
 480                  $backtrace = $errorAsException->getTrace();
 481                  $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
 482                  ($this->configureException)($errorAsException, $lightTrace, $file, $line);
 483              } else {
 484                  ($this->configureException)($errorAsException, []);
 485                  $backtrace = [];
 486              }
 487          }
 488  
 489          if ($throw) {
 490              if (\PHP_VERSION_ID < 70400 && \E_USER_ERROR & $type) {
 491                  for ($i = 1; isset($backtrace[$i]); ++$i) {
 492                      if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
 493                          && '__toString' === $backtrace[$i]['function']
 494                          && '->' === $backtrace[$i]['type']
 495                          && !isset($backtrace[$i - 1]['class'])
 496                          && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
 497                      ) {
 498                          // Here, we know trigger_error() has been called from __toString().
 499                          // PHP triggers a fatal error when throwing from __toString().
 500                          // A small convention allows working around the limitation:
 501                          // given a caught $e exception in __toString(), quitting the method with
 502                          // `return trigger_error($e, E_USER_ERROR);` allows this error handler
 503                          // to make $e get through the __toString() barrier.
 504  
 505                          $context = 4 < \func_num_args() ? (func_get_arg(4) ?: []) : [];
 506  
 507                          foreach ($context as $e) {
 508                              if ($e instanceof \Throwable && $e->__toString() === $message) {
 509                                  self::$toStringException = $e;
 510  
 511                                  return true;
 512                              }
 513                          }
 514  
 515                          // Display the original error message instead of the default one.
 516                          $this->handleException($errorAsException);
 517  
 518                          // Stop the process by giving back the error to the native handler.
 519                          return false;
 520                      }
 521                  }
 522              }
 523  
 524              throw $errorAsException;
 525          }
 526  
 527          if ($this->isRecursive) {
 528              $log = 0;
 529          } else {
 530              if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) {
 531                  $currentErrorHandler = set_error_handler('var_dump');
 532                  restore_error_handler();
 533              }
 534  
 535              try {
 536                  $this->isRecursive = true;
 537                  $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
 538                  $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []);
 539              } finally {
 540                  $this->isRecursive = false;
 541  
 542                  if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404)) {
 543                      set_error_handler($currentErrorHandler);
 544                  }
 545              }
 546          }
 547  
 548          return !$silenced && $type && $log;
 549      }
 550  
 551      /**
 552       * Handles an exception by logging then forwarding it to another handler.
 553       *
 554       * @internal
 555       */
 556      public function handleException(\Throwable $exception)
 557      {
 558          $handlerException = null;
 559  
 560          if (!$exception instanceof FatalError) {
 561              self::$exitCode = 255;
 562  
 563              $type = ThrowableUtils::getSeverity($exception);
 564          } else {
 565              $type = $exception->getError()['type'];
 566          }
 567  
 568          if ($this->loggedErrors & $type) {
 569              if (false !== strpos($message = $exception->getMessage(), "@anonymous\0")) {
 570                  $message = $this->parseAnonymousClass($message);
 571              }
 572  
 573              if ($exception instanceof FatalError) {
 574                  $message = 'Fatal '.$message;
 575              } elseif ($exception instanceof \Error) {
 576                  $message = 'Uncaught Error: '.$message;
 577              } elseif ($exception instanceof \ErrorException) {
 578                  $message = 'Uncaught '.$message;
 579              } else {
 580                  $message = 'Uncaught Exception: '.$message;
 581              }
 582  
 583              try {
 584                  $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
 585              } catch (\Throwable $handlerException) {
 586              }
 587          }
 588  
 589          if (!$exception instanceof OutOfMemoryError) {
 590              foreach ($this->getErrorEnhancers() as $errorEnhancer) {
 591                  if ($e = $errorEnhancer->enhance($exception)) {
 592                      $exception = $e;
 593                      break;
 594                  }
 595              }
 596          }
 597  
 598          $exceptionHandler = $this->exceptionHandler;
 599          $this->exceptionHandler = [$this, 'renderException'];
 600  
 601          if (null === $exceptionHandler || $exceptionHandler === $this->exceptionHandler) {
 602              $this->exceptionHandler = null;
 603          }
 604  
 605          try {
 606              if (null !== $exceptionHandler) {
 607                  return $exceptionHandler($exception);
 608              }
 609              $handlerException = $handlerException ?: $exception;
 610          } catch (\Throwable $handlerException) {
 611          }
 612          if ($exception === $handlerException && null === $this->exceptionHandler) {
 613              self::$reservedMemory = null; // Disable the fatal error handler
 614              throw $exception; // Give back $exception to the native handler
 615          }
 616  
 617          $loggedErrors = $this->loggedErrors;
 618          if ($exception === $handlerException) {
 619              $this->loggedErrors &= ~$type;
 620          }
 621  
 622          try {
 623              $this->handleException($handlerException);
 624          } finally {
 625              $this->loggedErrors = $loggedErrors;
 626          }
 627      }
 628  
 629      /**
 630       * Shutdown registered function for handling PHP fatal errors.
 631       *
 632       * @param array|null $error An array as returned by error_get_last()
 633       *
 634       * @internal
 635       */
 636      public static function handleFatalError(array $error = null): void
 637      {
 638          if (null === self::$reservedMemory) {
 639              return;
 640          }
 641  
 642          $handler = self::$reservedMemory = null;
 643          $handlers = [];
 644          $previousHandler = null;
 645          $sameHandlerLimit = 10;
 646  
 647          while (!\is_array($handler) || !$handler[0] instanceof self) {
 648              $handler = set_exception_handler('var_dump');
 649              restore_exception_handler();
 650  
 651              if (!$handler) {
 652                  break;
 653              }
 654              restore_exception_handler();
 655  
 656              if ($handler !== $previousHandler) {
 657                  array_unshift($handlers, $handler);
 658                  $previousHandler = $handler;
 659              } elseif (0 === --$sameHandlerLimit) {
 660                  $handler = null;
 661                  break;
 662              }
 663          }
 664          foreach ($handlers as $h) {
 665              set_exception_handler($h);
 666          }
 667          if (!$handler) {
 668              return;
 669          }
 670          if ($handler !== $h) {
 671              $handler[0]->setExceptionHandler($h);
 672          }
 673          $handler = $handler[0];
 674          $handlers = [];
 675  
 676          if ($exit = null === $error) {
 677              $error = error_get_last();
 678          }
 679  
 680          if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) {
 681              // Let's not throw anymore but keep logging
 682              $handler->throwAt(0, true);
 683              $trace = $error['backtrace'] ?? null;
 684  
 685              if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
 686                  $fatalError = new OutOfMemoryError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, false, $trace);
 687              } else {
 688                  $fatalError = new FatalError($handler->levels[$error['type']].': '.$error['message'], 0, $error, 2, true, $trace);
 689              }
 690          } else {
 691              $fatalError = null;
 692          }
 693  
 694          try {
 695              if (null !== $fatalError) {
 696                  self::$exitCode = 255;
 697                  $handler->handleException($fatalError);
 698              }
 699          } catch (FatalError $e) {
 700              // Ignore this re-throw
 701          }
 702  
 703          if ($exit && self::$exitCode) {
 704              $exitCode = self::$exitCode;
 705              register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
 706          }
 707      }
 708  
 709      /**
 710       * Renders the given exception.
 711       *
 712       * As this method is mainly called during boot where nothing is yet available,
 713       * the output is always either HTML or CLI depending where PHP runs.
 714       */
 715      private function renderException(\Throwable $exception): void
 716      {
 717          $renderer = \in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) ? new CliErrorRenderer() : new HtmlErrorRenderer($this->debug);
 718  
 719          $exception = $renderer->render($exception);
 720  
 721          if (!headers_sent()) {
 722              http_response_code($exception->getStatusCode());
 723  
 724              foreach ($exception->getHeaders() as $name => $value) {
 725                  header($name.': '.$value, false);
 726              }
 727          }
 728  
 729          echo $exception->getAsString();
 730      }
 731  
 732      /**
 733       * Override this method if you want to define more error enhancers.
 734       *
 735       * @return ErrorEnhancerInterface[]
 736       */
 737      protected function getErrorEnhancers(): iterable
 738      {
 739          return [
 740              new UndefinedFunctionErrorEnhancer(),
 741              new UndefinedMethodErrorEnhancer(),
 742              new ClassNotFoundErrorEnhancer(),
 743          ];
 744      }
 745  
 746      /**
 747       * Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader.
 748       */
 749      private function cleanTrace(array $backtrace, int $type, string &$file, int &$line, bool $throw): array
 750      {
 751          $lightTrace = $backtrace;
 752  
 753          for ($i = 0; isset($backtrace[$i]); ++$i) {
 754              if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
 755                  $lightTrace = \array_slice($lightTrace, 1 + $i);
 756                  break;
 757              }
 758          }
 759          if (\E_USER_DEPRECATED === $type) {
 760              for ($i = 0; isset($lightTrace[$i]); ++$i) {
 761                  if (!isset($lightTrace[$i]['file'], $lightTrace[$i]['line'], $lightTrace[$i]['function'])) {
 762                      continue;
 763                  }
 764                  if (!isset($lightTrace[$i]['class']) && 'trigger_deprecation' === $lightTrace[$i]['function']) {
 765                      $file = $lightTrace[$i]['file'];
 766                      $line = $lightTrace[$i]['line'];
 767                      $lightTrace = \array_slice($lightTrace, 1 + $i);
 768                      break;
 769                  }
 770              }
 771          }
 772          if (class_exists(DebugClassLoader::class, false)) {
 773              for ($i = \count($lightTrace) - 2; 0 < $i; --$i) {
 774                  if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) {
 775                      array_splice($lightTrace, --$i, 2);
 776                  }
 777              }
 778          }
 779          if (!($throw || $this->scopedErrors & $type)) {
 780              for ($i = 0; isset($lightTrace[$i]); ++$i) {
 781                  unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
 782              }
 783          }
 784  
 785          return $lightTrace;
 786      }
 787  
 788      /**
 789       * Parse the error message by removing the anonymous class notation
 790       * and using the parent class instead if possible.
 791       */
 792      private function parseAnonymousClass(string $message): string
 793      {
 794          return preg_replace_callback('/[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', static function ($m) {
 795              return class_exists($m[0], false) ? (get_parent_class($m[0]) ?: key(class_implements($m[0])) ?: 'class').'@anonymous' : $m[0];
 796          }, $message);
 797      }
 798  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer