[ Index ] |
PHP Cross Reference of phpBB-3.3.12-deutsch |
[Summary view] [Print] [Text view]
1 <?php 2 3 /* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 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\Debug; 13 14 use Psr\Log\LoggerInterface; 15 use Psr\Log\LogLevel; 16 use Symfony\Component\Debug\Exception\ContextErrorException; 17 use Symfony\Component\Debug\Exception\FatalErrorException; 18 use Symfony\Component\Debug\Exception\FatalThrowableError; 19 use Symfony\Component\Debug\Exception\OutOfMemoryException; 20 use Symfony\Component\Debug\Exception\SilencedErrorContext; 21 use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; 22 use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; 23 use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; 24 use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; 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 <p@tchwork.com> 47 * @author Grégoire Pineau <lyrixx@lyrixx.info> 48 */ 49 class ErrorHandler 50 { 51 private $levels = [ 52 \E_DEPRECATED => 'Deprecated', 53 \E_USER_DEPRECATED => 'User Deprecated', 54 \E_NOTICE => 'Notice', 55 \E_USER_NOTICE => 'User Notice', 56 \E_STRICT => 'Runtime Notice', 57 \E_WARNING => 'Warning', 58 \E_USER_WARNING => 'User Warning', 59 \E_COMPILE_WARNING => 'Compile Warning', 60 \E_CORE_WARNING => 'Core Warning', 61 \E_USER_ERROR => 'User Error', 62 \E_RECOVERABLE_ERROR => 'Catchable Fatal Error', 63 \E_COMPILE_ERROR => 'Compile Error', 64 \E_PARSE => 'Parse Error', 65 \E_ERROR => 'Error', 66 \E_CORE_ERROR => 'Core Error', 67 ]; 68 69 private $loggers = [ 70 \E_DEPRECATED => [null, LogLevel::INFO], 71 \E_USER_DEPRECATED => [null, LogLevel::INFO], 72 \E_NOTICE => [null, LogLevel::WARNING], 73 \E_USER_NOTICE => [null, LogLevel::WARNING], 74 \E_STRICT => [null, LogLevel::WARNING], 75 \E_WARNING => [null, LogLevel::WARNING], 76 \E_USER_WARNING => [null, LogLevel::WARNING], 77 \E_COMPILE_WARNING => [null, LogLevel::WARNING], 78 \E_CORE_WARNING => [null, LogLevel::WARNING], 79 \E_USER_ERROR => [null, LogLevel::CRITICAL], 80 \E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL], 81 \E_COMPILE_ERROR => [null, LogLevel::CRITICAL], 82 \E_PARSE => [null, LogLevel::CRITICAL], 83 \E_ERROR => [null, LogLevel::CRITICAL], 84 \E_CORE_ERROR => [null, LogLevel::CRITICAL], 85 ]; 86 87 private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED 88 private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED 89 private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE 90 private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE 91 private $loggedErrors = 0; 92 private $traceReflector; 93 94 private $isRecursive = 0; 95 private $isRoot = false; 96 private $exceptionHandler; 97 private $bootstrappingLogger; 98 99 private static $reservedMemory; 100 private static $stackedErrors = []; 101 private static $stackedErrorLevels = []; 102 private static $toStringException = null; 103 private static $silencedErrorCache = []; 104 private static $silencedErrorCount = 0; 105 private static $exitCode = 0; 106 107 /** 108 * Registers the error handler. 109 * 110 * @param self|null $handler The handler to register 111 * @param bool $replace Whether to replace or not any existing handler 112 * 113 * @return self The registered error handler 114 */ 115 public static function register(self $handler = null, $replace = true) 116 { 117 if (null === self::$reservedMemory) { 118 self::$reservedMemory = str_repeat('x', 10240); 119 register_shutdown_function(__CLASS__.'::handleFatalError'); 120 } 121 122 if ($handlerIsNew = null === $handler) { 123 $handler = new static(); 124 } 125 126 if (null === $prev = set_error_handler([$handler, 'handleError'])) { 127 restore_error_handler(); 128 // Specifying the error types earlier would expose us to https://bugs.php.net/63206 129 set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors); 130 $handler->isRoot = true; 131 } 132 133 if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) { 134 $handler = $prev[0]; 135 $replace = false; 136 } 137 if (!$replace && $prev) { 138 restore_error_handler(); 139 $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; 140 } else { 141 $handlerIsRegistered = true; 142 } 143 if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) { 144 restore_exception_handler(); 145 if (!$handlerIsRegistered) { 146 $handler = $prev[0]; 147 } elseif ($handler !== $prev[0] && $replace) { 148 set_exception_handler([$handler, 'handleException']); 149 $p = $prev[0]->setExceptionHandler(null); 150 $handler->setExceptionHandler($p); 151 $prev[0]->setExceptionHandler($p); 152 } 153 } else { 154 $handler->setExceptionHandler($prev); 155 } 156 157 $handler->throwAt(\E_ALL & $handler->thrownErrors, true); 158 159 return $handler; 160 } 161 162 public function __construct(BufferingLogger $bootstrappingLogger = null) 163 { 164 if ($bootstrappingLogger) { 165 $this->bootstrappingLogger = $bootstrappingLogger; 166 $this->setDefaultLogger($bootstrappingLogger); 167 } 168 $this->traceReflector = new \ReflectionProperty('Exception', 'trace'); 169 $this->traceReflector->setAccessible(true); 170 } 171 172 /** 173 * Sets a logger to non assigned errors levels. 174 * 175 * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels 176 * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants 177 * @param bool $replace Whether to replace or not any existing logger 178 */ 179 public function setDefaultLogger(LoggerInterface $logger, $levels = \E_ALL, $replace = false) 180 { 181 $loggers = []; 182 183 if (\is_array($levels)) { 184 foreach ($levels as $type => $logLevel) { 185 if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { 186 $loggers[$type] = [$logger, $logLevel]; 187 } 188 } 189 } else { 190 if (null === $levels) { 191 $levels = \E_ALL; 192 } 193 foreach ($this->loggers as $type => $log) { 194 if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { 195 $log[0] = $logger; 196 $loggers[$type] = $log; 197 } 198 } 199 } 200 201 $this->setLoggers($loggers); 202 } 203 204 /** 205 * Sets a logger for each error level. 206 * 207 * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map 208 * 209 * @return array The previous map 210 * 211 * @throws \InvalidArgumentException 212 */ 213 public function setLoggers(array $loggers) 214 { 215 $prevLogged = $this->loggedErrors; 216 $prev = $this->loggers; 217 $flush = []; 218 219 foreach ($loggers as $type => $log) { 220 if (!isset($prev[$type])) { 221 throw new \InvalidArgumentException('Unknown error type: '.$type); 222 } 223 if (!\is_array($log)) { 224 $log = [$log]; 225 } elseif (!\array_key_exists(0, $log)) { 226 throw new \InvalidArgumentException('No logger provided.'); 227 } 228 if (null === $log[0]) { 229 $this->loggedErrors &= ~$type; 230 } elseif ($log[0] instanceof LoggerInterface) { 231 $this->loggedErrors |= $type; 232 } else { 233 throw new \InvalidArgumentException('Invalid logger provided.'); 234 } 235 $this->loggers[$type] = $log + $prev[$type]; 236 237 if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { 238 $flush[$type] = $type; 239 } 240 } 241 $this->reRegister($prevLogged | $this->thrownErrors); 242 243 if ($flush) { 244 foreach ($this->bootstrappingLogger->cleanLogs() as $log) { 245 $type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : \E_ERROR; 246 if (!isset($flush[$type])) { 247 $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); 248 } elseif ($this->loggers[$type][0]) { 249 $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); 250 } 251 } 252 } 253 254 return $prev; 255 } 256 257 /** 258 * Sets a user exception handler. 259 * 260 * @param callable $handler A handler that will be called on Exception 261 * 262 * @return callable|null The previous exception handler 263 */ 264 public function setExceptionHandler(callable $handler = null) 265 { 266 $prev = $this->exceptionHandler; 267 $this->exceptionHandler = $handler; 268 269 return $prev; 270 } 271 272 /** 273 * Sets the PHP error levels that throw an exception when a PHP error occurs. 274 * 275 * @param int $levels A bit field of E_* constants for thrown errors 276 * @param bool $replace Replace or amend the previous value 277 * 278 * @return int The previous value 279 */ 280 public function throwAt($levels, $replace = false) 281 { 282 $prev = $this->thrownErrors; 283 $this->thrownErrors = ($levels | \E_RECOVERABLE_ERROR | \E_USER_ERROR) & ~\E_USER_DEPRECATED & ~\E_DEPRECATED; 284 if (!$replace) { 285 $this->thrownErrors |= $prev; 286 } 287 $this->reRegister($prev | $this->loggedErrors); 288 289 return $prev; 290 } 291 292 /** 293 * Sets the PHP error levels for which local variables are preserved. 294 * 295 * @param int $levels A bit field of E_* constants for scoped errors 296 * @param bool $replace Replace or amend the previous value 297 * 298 * @return int The previous value 299 */ 300 public function scopeAt($levels, $replace = false) 301 { 302 $prev = $this->scopedErrors; 303 $this->scopedErrors = (int) $levels; 304 if (!$replace) { 305 $this->scopedErrors |= $prev; 306 } 307 308 return $prev; 309 } 310 311 /** 312 * Sets the PHP error levels for which the stack trace is preserved. 313 * 314 * @param int $levels A bit field of E_* constants for traced errors 315 * @param bool $replace Replace or amend the previous value 316 * 317 * @return int The previous value 318 */ 319 public function traceAt($levels, $replace = false) 320 { 321 $prev = $this->tracedErrors; 322 $this->tracedErrors = (int) $levels; 323 if (!$replace) { 324 $this->tracedErrors |= $prev; 325 } 326 327 return $prev; 328 } 329 330 /** 331 * Sets the error levels where the @-operator is ignored. 332 * 333 * @param int $levels A bit field of E_* constants for screamed errors 334 * @param bool $replace Replace or amend the previous value 335 * 336 * @return int The previous value 337 */ 338 public function screamAt($levels, $replace = false) 339 { 340 $prev = $this->screamedErrors; 341 $this->screamedErrors = (int) $levels; 342 if (!$replace) { 343 $this->screamedErrors |= $prev; 344 } 345 346 return $prev; 347 } 348 349 /** 350 * Re-registers as a PHP error handler if levels changed. 351 */ 352 private function reRegister($prev) 353 { 354 if ($prev !== $this->thrownErrors | $this->loggedErrors) { 355 $handler = set_error_handler('var_dump'); 356 $handler = \is_array($handler) ? $handler[0] : null; 357 restore_error_handler(); 358 if ($handler === $this) { 359 restore_error_handler(); 360 if ($this->isRoot) { 361 set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors); 362 } else { 363 set_error_handler([$this, 'handleError']); 364 } 365 } 366 } 367 } 368 369 /** 370 * Handles errors by filtering then logging them according to the configured bit fields. 371 * 372 * @param int $type One of the E_* constants 373 * @param string $message 374 * @param string $file 375 * @param int $line 376 * 377 * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself 378 * 379 * @throws \ErrorException When $this->thrownErrors requests so 380 * 381 * @internal 382 */ 383 public function handleError($type, $message, $file, $line) 384 { 385 if (\PHP_VERSION_ID >= 70300 && \E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) { 386 $type = \E_DEPRECATED; 387 } 388 389 // Level is the current error reporting level to manage silent error. 390 $level = error_reporting(); 391 $silenced = 0 === ($level & $type); 392 // Strong errors are not authorized to be silenced. 393 $level |= \E_RECOVERABLE_ERROR | \E_USER_ERROR | \E_DEPRECATED | \E_USER_DEPRECATED; 394 $log = $this->loggedErrors & $type; 395 $throw = $this->thrownErrors & $type & $level; 396 $type &= $level | $this->screamedErrors; 397 398 if (!$type || (!$log && !$throw)) { 399 return !$silenced && $type && $log; 400 } 401 $scope = $this->scopedErrors & $type; 402 403 if (4 < $numArgs = \func_num_args()) { 404 $context = func_get_arg(4) ?: []; 405 $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM 406 } else { 407 $context = []; 408 $backtrace = null; 409 } 410 411 if (isset($context['GLOBALS']) && $scope) { 412 $e = $context; // Whatever the signature of the method, 413 unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 414 $context = $e; 415 } 416 417 if (null !== $backtrace && $type & \E_ERROR) { 418 // E_ERROR fatal errors are triggered on HHVM when 419 // hhvm.error_handling.call_user_handler_on_fatals=1 420 // which is the way to get their backtrace. 421 $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); 422 423 return true; 424 } 425 426 $logMessage = $this->levels[$type].': '.$message; 427 428 if (null !== self::$toStringException) { 429 $errorAsException = self::$toStringException; 430 self::$toStringException = null; 431 } elseif (!$throw && !($type & $level)) { 432 if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) { 433 $lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : []; 434 $errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace); 435 } elseif (isset(self::$silencedErrorCache[$id][$message])) { 436 $lightTrace = null; 437 $errorAsException = self::$silencedErrorCache[$id][$message]; 438 ++$errorAsException->count; 439 } else { 440 $lightTrace = []; 441 $errorAsException = null; 442 } 443 444 if (100 < ++self::$silencedErrorCount) { 445 self::$silencedErrorCache = $lightTrace = []; 446 self::$silencedErrorCount = 1; 447 } 448 if ($errorAsException) { 449 self::$silencedErrorCache[$id][$message] = $errorAsException; 450 } 451 if (null === $lightTrace) { 452 return true; 453 } 454 } else { 455 if ($scope) { 456 $errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context); 457 } else { 458 $errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line); 459 } 460 461 // Clean the trace by removing function arguments and the first frames added by the error handler itself. 462 if ($throw || $this->tracedErrors & $type) { 463 $backtrace = $backtrace ?: $errorAsException->getTrace(); 464 $lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw); 465 $this->traceReflector->setValue($errorAsException, $lightTrace); 466 } else { 467 $this->traceReflector->setValue($errorAsException, []); 468 } 469 } 470 471 if ($throw) { 472 if (\PHP_VERSION_ID < 70400 && \E_USER_ERROR & $type) { 473 for ($i = 1; isset($backtrace[$i]); ++$i) { 474 if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) 475 && '__toString' === $backtrace[$i]['function'] 476 && '->' === $backtrace[$i]['type'] 477 && !isset($backtrace[$i - 1]['class']) 478 && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) 479 ) { 480 // Here, we know trigger_error() has been called from __toString(). 481 // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. 482 // A small convention allows working around the limitation: 483 // given a caught $e exception in __toString(), quitting the method with 484 // `return trigger_error($e, E_USER_ERROR);` allows this error handler 485 // to make $e get through the __toString() barrier. 486 487 foreach ($context as $e) { 488 if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { 489 if (1 === $i) { 490 // On HHVM 491 $errorAsException = $e; 492 break; 493 } 494 self::$toStringException = $e; 495 496 return true; 497 } 498 } 499 500 if (1 < $i) { 501 // On PHP (not on HHVM), display the original error message instead of the default one. 502 $this->handleException($errorAsException); 503 504 // Stop the process by giving back the error to the native handler. 505 return false; 506 } 507 } 508 } 509 } 510 511 throw $errorAsException; 512 } 513 514 if ($this->isRecursive) { 515 $log = 0; 516 } elseif (self::$stackedErrorLevels) { 517 self::$stackedErrors[] = [ 518 $this->loggers[$type][0], 519 ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, 520 $logMessage, 521 $errorAsException ? ['exception' => $errorAsException] : [], 522 ]; 523 } else { 524 if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { 525 $currentErrorHandler = set_error_handler('var_dump'); 526 restore_error_handler(); 527 } 528 529 try { 530 $this->isRecursive = true; 531 $level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG; 532 $this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []); 533 } finally { 534 $this->isRecursive = false; 535 536 if (\PHP_VERSION_ID < (\PHP_VERSION_ID < 70400 ? 70316 : 70404) && !\defined('HHVM_VERSION')) { 537 set_error_handler($currentErrorHandler); 538 } 539 } 540 } 541 542 return !$silenced && $type && $log; 543 } 544 545 /** 546 * Handles an exception by logging then forwarding it to another handler. 547 * 548 * @param \Exception|\Throwable $exception An exception to handle 549 * @param array $error An array as returned by error_get_last() 550 * 551 * @internal 552 */ 553 public function handleException($exception, array $error = null) 554 { 555 if (null === $error) { 556 self::$exitCode = 255; 557 } 558 if (!$exception instanceof \Exception) { 559 $exception = new FatalThrowableError($exception); 560 } 561 $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : \E_ERROR; 562 $handlerException = null; 563 564 if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { 565 if ($exception instanceof FatalErrorException) { 566 if ($exception instanceof FatalThrowableError) { 567 $error = [ 568 'type' => $type, 569 'message' => $message = $exception->getMessage(), 570 'file' => $exception->getFile(), 571 'line' => $exception->getLine(), 572 ]; 573 } else { 574 $message = 'Fatal '.$exception->getMessage(); 575 } 576 } elseif ($exception instanceof \ErrorException) { 577 $message = 'Uncaught '.$exception->getMessage(); 578 } else { 579 $message = 'Uncaught Exception: '.$exception->getMessage(); 580 } 581 } 582 if ($this->loggedErrors & $type) { 583 try { 584 $this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]); 585 } catch (\Exception $handlerException) { 586 } catch (\Throwable $handlerException) { 587 } 588 } 589 if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { 590 foreach ($this->getFatalErrorHandlers() as $handler) { 591 if ($e = $handler->handleError($error, $exception)) { 592 $exception = $e; 593 break; 594 } 595 } 596 } 597 $exceptionHandler = $this->exceptionHandler; 598 $this->exceptionHandler = null; 599 try { 600 if (null !== $exceptionHandler) { 601 $exceptionHandler($exception); 602 603 return; 604 } 605 $handlerException = $handlerException ?: $exception; 606 } catch (\Exception $handlerException) { 607 } catch (\Throwable $handlerException) { 608 } 609 if ($exception === $handlerException) { 610 self::$reservedMemory = null; // Disable the fatal error handler 611 throw $exception; // Give back $exception to the native handler 612 } 613 $this->handleException($handlerException); 614 } 615 616 /** 617 * Shutdown registered function for handling PHP fatal errors. 618 * 619 * @param array $error An array as returned by error_get_last() 620 * 621 * @internal 622 */ 623 public static function handleFatalError(array $error = null) 624 { 625 if (null === self::$reservedMemory) { 626 return; 627 } 628 629 $handler = self::$reservedMemory = null; 630 $handlers = []; 631 $previousHandler = null; 632 $sameHandlerLimit = 10; 633 634 while (!\is_array($handler) || !$handler[0] instanceof self) { 635 $handler = set_exception_handler('var_dump'); 636 restore_exception_handler(); 637 638 if (!$handler) { 639 break; 640 } 641 restore_exception_handler(); 642 643 if ($handler !== $previousHandler) { 644 array_unshift($handlers, $handler); 645 $previousHandler = $handler; 646 } elseif (0 === --$sameHandlerLimit) { 647 $handler = null; 648 break; 649 } 650 } 651 foreach ($handlers as $h) { 652 set_exception_handler($h); 653 } 654 if (!$handler) { 655 return; 656 } 657 if ($handler !== $h) { 658 $handler[0]->setExceptionHandler($h); 659 } 660 $handler = $handler[0]; 661 $handlers = []; 662 663 if ($exit = null === $error) { 664 $error = error_get_last(); 665 } 666 667 try { 668 while (self::$stackedErrorLevels) { 669 static::unstackErrors(); 670 } 671 } catch (\Exception $exception) { 672 // Handled below 673 } catch (\Throwable $exception) { 674 // Handled below 675 } 676 677 if ($error && $error['type'] &= \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR) { 678 // Let's not throw anymore but keep logging 679 $handler->throwAt(0, true); 680 $trace = isset($error['backtrace']) ? $error['backtrace'] : null; 681 682 if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { 683 $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); 684 } else { 685 $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); 686 } 687 } 688 689 try { 690 if (isset($exception)) { 691 self::$exitCode = 255; 692 $handler->handleException($exception, $error); 693 } 694 } catch (FatalErrorException $e) { 695 // Ignore this re-throw 696 } 697 698 if ($exit && self::$exitCode) { 699 $exitCode = self::$exitCode; 700 register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); 701 } 702 } 703 704 /** 705 * Configures the error handler for delayed handling. 706 * Ensures also that non-catchable fatal errors are never silenced. 707 * 708 * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 709 * PHP has a compile stage where it behaves unusually. To workaround it, 710 * we plug an error handler that only stacks errors for later. 711 * 712 * The most important feature of this is to prevent 713 * autoloading until unstackErrors() is called. 714 * 715 * @deprecated since version 3.4, to be removed in 4.0. 716 */ 717 public static function stackErrors() 718 { 719 @trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED); 720 721 self::$stackedErrorLevels[] = error_reporting(error_reporting() | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR); 722 } 723 724 /** 725 * Unstacks stacked errors and forwards to the logger. 726 * 727 * @deprecated since version 3.4, to be removed in 4.0. 728 */ 729 public static function unstackErrors() 730 { 731 @trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', \E_USER_DEPRECATED); 732 733 $level = array_pop(self::$stackedErrorLevels); 734 735 if (null !== $level) { 736 $errorReportingLevel = error_reporting($level); 737 if ($errorReportingLevel !== ($level | \E_PARSE | \E_ERROR | \E_CORE_ERROR | \E_COMPILE_ERROR)) { 738 // If the user changed the error level, do not overwrite it 739 error_reporting($errorReportingLevel); 740 } 741 } 742 743 if (empty(self::$stackedErrorLevels)) { 744 $errors = self::$stackedErrors; 745 self::$stackedErrors = []; 746 747 foreach ($errors as $error) { 748 $error[0]->log($error[1], $error[2], $error[3]); 749 } 750 } 751 } 752 753 /** 754 * Gets the fatal error handlers. 755 * 756 * Override this method if you want to define more fatal error handlers. 757 * 758 * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface 759 */ 760 protected function getFatalErrorHandlers() 761 { 762 return [ 763 new UndefinedFunctionFatalErrorHandler(), 764 new UndefinedMethodFatalErrorHandler(), 765 new ClassNotFoundFatalErrorHandler(), 766 ]; 767 } 768 769 private function cleanTrace($backtrace, $type, $file, $line, $throw) 770 { 771 $lightTrace = $backtrace; 772 773 for ($i = 0; isset($backtrace[$i]); ++$i) { 774 if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { 775 $lightTrace = \array_slice($lightTrace, 1 + $i); 776 break; 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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Jun 23 12:25:44 2024 | Cross-referenced by PHPXref 0.7.1 |