[ Index ] |
PHP Cross Reference of phpBB-3.2.11-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\FatalErrorHandler\ClassNotFoundFatalErrorHandler; 21 use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; 22 use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler; 23 use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler; 24 25 /** 26 * A generic ErrorHandler for the PHP engine. 27 * 28 * Provides five bit fields that control how errors are handled: 29 * - thrownErrors: errors thrown as \ErrorException 30 * - loggedErrors: logged errors, when not @-silenced 31 * - scopedErrors: errors thrown or logged with their local context 32 * - tracedErrors: errors logged with their stack trace, only once for repeated errors 33 * - screamedErrors: never @-silenced errors 34 * 35 * Each error level can be logged by a dedicated PSR-3 logger object. 36 * Screaming only applies to logging. 37 * Throwing takes precedence over logging. 38 * Uncaught exceptions are logged as E_ERROR. 39 * E_DEPRECATED and E_USER_DEPRECATED levels never throw. 40 * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. 41 * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. 42 * As errors have a performance cost, repeated errors are all logged, so that the developer 43 * can see them and weight them as more important to fix than others of the same level. 44 * 45 * @author Nicolas Grekas <p@tchwork.com> 46 */ 47 class ErrorHandler 48 { 49 /** 50 * @deprecated since version 2.6, to be removed in 3.0. 51 */ 52 const TYPE_DEPRECATION = -100; 53 54 private $levels = array( 55 E_DEPRECATED => 'Deprecated', 56 E_USER_DEPRECATED => 'User Deprecated', 57 E_NOTICE => 'Notice', 58 E_USER_NOTICE => 'User Notice', 59 E_STRICT => 'Runtime Notice', 60 E_WARNING => 'Warning', 61 E_USER_WARNING => 'User Warning', 62 E_COMPILE_WARNING => 'Compile Warning', 63 E_CORE_WARNING => 'Core Warning', 64 E_USER_ERROR => 'User Error', 65 E_RECOVERABLE_ERROR => 'Catchable Fatal Error', 66 E_COMPILE_ERROR => 'Compile Error', 67 E_PARSE => 'Parse Error', 68 E_ERROR => 'Error', 69 E_CORE_ERROR => 'Core Error', 70 ); 71 72 private $loggers = array( 73 E_DEPRECATED => array(null, LogLevel::INFO), 74 E_USER_DEPRECATED => array(null, LogLevel::INFO), 75 E_NOTICE => array(null, LogLevel::WARNING), 76 E_USER_NOTICE => array(null, LogLevel::WARNING), 77 E_STRICT => array(null, LogLevel::WARNING), 78 E_WARNING => array(null, LogLevel::WARNING), 79 E_USER_WARNING => array(null, LogLevel::WARNING), 80 E_COMPILE_WARNING => array(null, LogLevel::WARNING), 81 E_CORE_WARNING => array(null, LogLevel::WARNING), 82 E_USER_ERROR => array(null, LogLevel::CRITICAL), 83 E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL), 84 E_COMPILE_ERROR => array(null, LogLevel::CRITICAL), 85 E_PARSE => array(null, LogLevel::CRITICAL), 86 E_ERROR => array(null, LogLevel::CRITICAL), 87 E_CORE_ERROR => array(null, LogLevel::CRITICAL), 88 ); 89 90 private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED 91 private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED 92 private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE 93 private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE 94 private $loggedErrors = 0; 95 96 private $loggedTraces = array(); 97 private $isRecursive = 0; 98 private $isRoot = false; 99 private $exceptionHandler; 100 private $bootstrappingLogger; 101 102 private static $reservedMemory; 103 private static $stackedErrors = array(); 104 private static $stackedErrorLevels = array(); 105 private static $toStringException = null; 106 private static $exitCode = 0; 107 108 /** 109 * Same init value as thrownErrors. 110 * 111 * @deprecated since version 2.6, to be removed in 3.0. 112 */ 113 private $displayErrors = 0x1FFF; 114 115 /** 116 * Registers the error handler. 117 * 118 * @param self|int|null $handler The handler to register, or @deprecated (since version 2.6, to be removed in 3.0) bit field of thrown levels 119 * @param bool $replace Whether to replace or not any existing handler 120 * 121 * @return self The registered error handler 122 */ 123 public static function register($handler = null, $replace = true) 124 { 125 if (null === self::$reservedMemory) { 126 self::$reservedMemory = str_repeat('x', 10240); 127 register_shutdown_function(__CLASS__.'::handleFatalError'); 128 } 129 130 $levels = -1; 131 132 if ($handlerIsNew = !$handler instanceof self) { 133 // @deprecated polymorphism, to be removed in 3.0 134 if (null !== $handler) { 135 $levels = $replace ? $handler : 0; 136 $replace = true; 137 } 138 $handler = new static(); 139 } 140 141 if (null === $prev = set_error_handler(array($handler, 'handleError'))) { 142 restore_error_handler(); 143 // Specifying the error types earlier would expose us to https://bugs.php.net/63206 144 set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors); 145 $handler->isRoot = true; 146 } 147 148 if ($handlerIsNew && \is_array($prev) && $prev[0] instanceof self) { 149 $handler = $prev[0]; 150 $replace = false; 151 } 152 if (!$replace && $prev) { 153 restore_error_handler(); 154 $handlerIsRegistered = \is_array($prev) && $handler === $prev[0]; 155 } else { 156 $handlerIsRegistered = true; 157 } 158 if (\is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) { 159 restore_exception_handler(); 160 if (!$handlerIsRegistered) { 161 $handler = $prev[0]; 162 } elseif ($handler !== $prev[0] && $replace) { 163 set_exception_handler(array($handler, 'handleException')); 164 $p = $prev[0]->setExceptionHandler(null); 165 $handler->setExceptionHandler($p); 166 $prev[0]->setExceptionHandler($p); 167 } 168 } else { 169 $handler->setExceptionHandler($prev); 170 } 171 172 $handler->throwAt($levels & $handler->thrownErrors, true); 173 174 return $handler; 175 } 176 177 public function __construct(BufferingLogger $bootstrappingLogger = null) 178 { 179 if ($bootstrappingLogger) { 180 $this->bootstrappingLogger = $bootstrappingLogger; 181 $this->setDefaultLogger($bootstrappingLogger); 182 } 183 } 184 185 /** 186 * Sets a logger to non assigned errors levels. 187 * 188 * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels 189 * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants 190 * @param bool $replace Whether to replace or not any existing logger 191 */ 192 public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false) 193 { 194 $loggers = array(); 195 196 if (\is_array($levels)) { 197 foreach ($levels as $type => $logLevel) { 198 if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) { 199 $loggers[$type] = array($logger, $logLevel); 200 } 201 } 202 } else { 203 if (null === $levels) { 204 $levels = E_ALL | E_STRICT; 205 } 206 foreach ($this->loggers as $type => $log) { 207 if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) { 208 $log[0] = $logger; 209 $loggers[$type] = $log; 210 } 211 } 212 } 213 214 $this->setLoggers($loggers); 215 } 216 217 /** 218 * Sets a logger for each error level. 219 * 220 * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map 221 * 222 * @return array The previous map 223 * 224 * @throws \InvalidArgumentException 225 */ 226 public function setLoggers(array $loggers) 227 { 228 $prevLogged = $this->loggedErrors; 229 $prev = $this->loggers; 230 $flush = array(); 231 232 foreach ($loggers as $type => $log) { 233 if (!isset($prev[$type])) { 234 throw new \InvalidArgumentException('Unknown error type: '.$type); 235 } 236 if (!\is_array($log)) { 237 $log = array($log); 238 } elseif (!array_key_exists(0, $log)) { 239 throw new \InvalidArgumentException('No logger provided'); 240 } 241 if (null === $log[0]) { 242 $this->loggedErrors &= ~$type; 243 } elseif ($log[0] instanceof LoggerInterface) { 244 $this->loggedErrors |= $type; 245 } else { 246 throw new \InvalidArgumentException('Invalid logger provided'); 247 } 248 $this->loggers[$type] = $log + $prev[$type]; 249 250 if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) { 251 $flush[$type] = $type; 252 } 253 } 254 $this->reRegister($prevLogged | $this->thrownErrors); 255 256 if ($flush) { 257 foreach ($this->bootstrappingLogger->cleanLogs() as $log) { 258 $type = $log[2]['type']; 259 if (!isset($flush[$type])) { 260 $this->bootstrappingLogger->log($log[0], $log[1], $log[2]); 261 } elseif ($this->loggers[$type][0]) { 262 $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]); 263 } 264 } 265 } 266 267 return $prev; 268 } 269 270 /** 271 * Sets a user exception handler. 272 * 273 * @param callable $handler A handler that will be called on Exception 274 * 275 * @return callable|null The previous exception handler 276 * 277 * @throws \InvalidArgumentException 278 */ 279 public function setExceptionHandler($handler) 280 { 281 if (null !== $handler && !\is_callable($handler)) { 282 throw new \LogicException('The exception handler must be a valid PHP callable.'); 283 } 284 $prev = $this->exceptionHandler; 285 $this->exceptionHandler = $handler; 286 287 return $prev; 288 } 289 290 /** 291 * Sets the PHP error levels that throw an exception when a PHP error occurs. 292 * 293 * @param int $levels A bit field of E_* constants for thrown errors 294 * @param bool $replace Replace or amend the previous value 295 * 296 * @return int The previous value 297 */ 298 public function throwAt($levels, $replace = false) 299 { 300 $prev = $this->thrownErrors; 301 $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; 302 if (!$replace) { 303 $this->thrownErrors |= $prev; 304 } 305 $this->reRegister($prev | $this->loggedErrors); 306 307 // $this->displayErrors is @deprecated since version 2.6 308 $this->displayErrors = $this->thrownErrors; 309 310 return $prev; 311 } 312 313 /** 314 * Sets the PHP error levels for which local variables are preserved. 315 * 316 * @param int $levels A bit field of E_* constants for scoped errors 317 * @param bool $replace Replace or amend the previous value 318 * 319 * @return int The previous value 320 */ 321 public function scopeAt($levels, $replace = false) 322 { 323 $prev = $this->scopedErrors; 324 $this->scopedErrors = (int) $levels; 325 if (!$replace) { 326 $this->scopedErrors |= $prev; 327 } 328 329 return $prev; 330 } 331 332 /** 333 * Sets the PHP error levels for which the stack trace is preserved. 334 * 335 * @param int $levels A bit field of E_* constants for traced errors 336 * @param bool $replace Replace or amend the previous value 337 * 338 * @return int The previous value 339 */ 340 public function traceAt($levels, $replace = false) 341 { 342 $prev = $this->tracedErrors; 343 $this->tracedErrors = (int) $levels; 344 if (!$replace) { 345 $this->tracedErrors |= $prev; 346 } 347 348 return $prev; 349 } 350 351 /** 352 * Sets the error levels where the @-operator is ignored. 353 * 354 * @param int $levels A bit field of E_* constants for screamed errors 355 * @param bool $replace Replace or amend the previous value 356 * 357 * @return int The previous value 358 */ 359 public function screamAt($levels, $replace = false) 360 { 361 $prev = $this->screamedErrors; 362 $this->screamedErrors = (int) $levels; 363 if (!$replace) { 364 $this->screamedErrors |= $prev; 365 } 366 367 return $prev; 368 } 369 370 /** 371 * Re-registers as a PHP error handler if levels changed. 372 */ 373 private function reRegister($prev) 374 { 375 if ($prev !== $this->thrownErrors | $this->loggedErrors) { 376 $handler = set_error_handler('var_dump'); 377 $handler = \is_array($handler) ? $handler[0] : null; 378 restore_error_handler(); 379 if ($handler === $this) { 380 restore_error_handler(); 381 if ($this->isRoot) { 382 set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); 383 } else { 384 set_error_handler(array($this, 'handleError')); 385 } 386 } 387 } 388 } 389 390 /** 391 * Handles errors by filtering then logging them according to the configured bit fields. 392 * 393 * @param int $type One of the E_* constants 394 * @param string $message 395 * @param string $file 396 * @param int $line 397 * 398 * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself 399 * 400 * @throws \ErrorException When $this->thrownErrors requests so 401 * 402 * @internal 403 */ 404 public function handleError($type, $message, $file, $line) 405 { 406 $level = error_reporting(); 407 $silenced = 0 === ($level & $type); 408 $level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED; 409 $log = $this->loggedErrors & $type; 410 $throw = $this->thrownErrors & $type & $level; 411 $type &= $level | $this->screamedErrors; 412 413 if (!$type || (!$log && !$throw)) { 414 return !$silenced && $type && $log; 415 } 416 $scope = $this->scopedErrors & $type; 417 418 if (4 < $numArgs = \func_num_args()) { 419 $context = $scope ? (func_get_arg(4) ?: array()) : array(); 420 $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM 421 } else { 422 $context = array(); 423 $backtrace = null; 424 } 425 426 if (isset($context['GLOBALS']) && $scope) { 427 $e = $context; // Whatever the signature of the method, 428 unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 429 $context = $e; 430 } 431 432 if (null !== $backtrace && $type & E_ERROR) { 433 // E_ERROR fatal errors are triggered on HHVM when 434 // hhvm.error_handling.call_user_handler_on_fatals=1 435 // which is the way to get their backtrace. 436 $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace')); 437 438 return true; 439 } 440 441 if ($throw) { 442 if (null !== self::$toStringException) { 443 $throw = self::$toStringException; 444 self::$toStringException = null; 445 } elseif ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { 446 // Checking for class existence is a work around for https://bugs.php.net/42098 447 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); 448 } else { 449 $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line); 450 } 451 452 if (\PHP_VERSION_ID <= 50407 && (\PHP_VERSION_ID >= 50400 || \PHP_VERSION_ID <= 50317)) { 453 // Exceptions thrown from error handlers are sometimes not caught by the exception 454 // handler and shutdown handlers are bypassed before 5.4.8/5.3.18. 455 // We temporarily re-enable display_errors to prevent any blank page related to this bug. 456 457 $throw->errorHandlerCanary = new ErrorHandlerCanary(); 458 } 459 460 if (E_USER_ERROR & $type) { 461 $backtrace = $backtrace ?: $throw->getTrace(); 462 463 for ($i = 1; isset($backtrace[$i]); ++$i) { 464 if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function']) 465 && '__toString' === $backtrace[$i]['function'] 466 && '->' === $backtrace[$i]['type'] 467 && !isset($backtrace[$i - 1]['class']) 468 && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function']) 469 ) { 470 // Here, we know trigger_error() has been called from __toString(). 471 // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead. 472 // A small convention allows working around the limitation: 473 // given a caught $e exception in __toString(), quitting the method with 474 // `return trigger_error($e, E_USER_ERROR);` allows this error handler 475 // to make $e get through the __toString() barrier. 476 477 foreach ($context as $e) { 478 if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) { 479 if (1 === $i) { 480 // On HHVM 481 $throw = $e; 482 break; 483 } 484 self::$toStringException = $e; 485 486 return true; 487 } 488 } 489 490 if (1 < $i) { 491 // On PHP (not on HHVM), display the original error message instead of the default one. 492 $this->handleException($throw); 493 494 // Stop the process by giving back the error to the native handler. 495 return false; 496 } 497 } 498 } 499 } 500 501 throw $throw; 502 } 503 504 // For duplicated errors, log the trace only once 505 $e = md5("{$type}/{$line}/{$file}\x00{$message}", true); 506 $trace = true; 507 508 if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) { 509 $trace = false; 510 } else { 511 $this->loggedTraces[$e] = 1; 512 } 513 514 $e = compact('type', 'file', 'line', 'level'); 515 516 if ($type & $level) { 517 if ($scope) { 518 $e['scope_vars'] = $context; 519 if ($trace) { 520 $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT); 521 } 522 } elseif ($trace) { 523 if (null === $backtrace) { 524 $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); 525 } else { 526 foreach ($backtrace as &$frame) { 527 unset($frame['args'], $frame); 528 } 529 $e['stack'] = $backtrace; 530 } 531 } 532 } 533 534 if ($this->isRecursive) { 535 $log = 0; 536 } elseif (self::$stackedErrorLevels) { 537 self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); 538 } else { 539 try { 540 $this->isRecursive = true; 541 $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e); 542 $this->isRecursive = false; 543 } catch (\Exception $e) { 544 $this->isRecursive = false; 545 546 throw $e; 547 } catch (\Throwable $e) { 548 $this->isRecursive = false; 549 550 throw $e; 551 } 552 } 553 554 return !$silenced && $type && $log; 555 } 556 557 /** 558 * Handles an exception by logging then forwarding it to another handler. 559 * 560 * @param \Exception|\Throwable $exception An exception to handle 561 * @param array $error An array as returned by error_get_last() 562 * 563 * @internal 564 */ 565 public function handleException($exception, array $error = null) 566 { 567 if (null === $error) { 568 self::$exitCode = 255; 569 } 570 if (!$exception instanceof \Exception) { 571 $exception = new FatalThrowableError($exception); 572 } 573 $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR; 574 $handlerException = null; 575 576 if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) { 577 $e = array( 578 'type' => $type, 579 'file' => $exception->getFile(), 580 'line' => $exception->getLine(), 581 'level' => error_reporting(), 582 'stack' => $exception->getTrace(), 583 ); 584 if ($exception instanceof FatalErrorException) { 585 if ($exception instanceof FatalThrowableError) { 586 $error = array( 587 'type' => $type, 588 'message' => $message = $exception->getMessage(), 589 'file' => $e['file'], 590 'line' => $e['line'], 591 ); 592 } else { 593 $message = 'Fatal '.$exception->getMessage(); 594 } 595 } elseif ($exception instanceof \ErrorException) { 596 $message = 'Uncaught '.$exception->getMessage(); 597 if ($exception instanceof ContextErrorException) { 598 $e['context'] = $exception->getContext(); 599 } 600 } else { 601 $message = 'Uncaught Exception: '.$exception->getMessage(); 602 } 603 } 604 if ($this->loggedErrors & $type) { 605 try { 606 $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e); 607 } catch (\Exception $handlerException) { 608 } catch (\Throwable $handlerException) { 609 } 610 } 611 if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { 612 foreach ($this->getFatalErrorHandlers() as $handler) { 613 if ($e = $handler->handleError($error, $exception)) { 614 $exception = $e; 615 break; 616 } 617 } 618 } 619 $exceptionHandler = $this->exceptionHandler; 620 $this->exceptionHandler = null; 621 try { 622 if (null !== $exceptionHandler) { 623 return \call_user_func($exceptionHandler, $exception); 624 } 625 $handlerException = $handlerException ?: $exception; 626 } catch (\Exception $handlerException) { 627 } catch (\Throwable $handlerException) { 628 } 629 if ($exception === $handlerException) { 630 self::$reservedMemory = null; // Disable the fatal error handler 631 throw $exception; // Give back $exception to the native handler 632 } 633 $this->handleException($handlerException); 634 } 635 636 /** 637 * Shutdown registered function for handling PHP fatal errors. 638 * 639 * @param array $error An array as returned by error_get_last() 640 * 641 * @internal 642 */ 643 public static function handleFatalError(array $error = null) 644 { 645 if (null === self::$reservedMemory) { 646 return; 647 } 648 649 $handler = self::$reservedMemory = null; 650 $handlers = array(); 651 $previousHandler = null; 652 $sameHandlerLimit = 10; 653 654 while (!\is_array($handler) || !$handler[0] instanceof self) { 655 $handler = set_exception_handler('var_dump'); 656 restore_exception_handler(); 657 658 if (!$handler) { 659 break; 660 } 661 restore_exception_handler(); 662 663 if ($handler !== $previousHandler) { 664 array_unshift($handlers, $handler); 665 $previousHandler = $handler; 666 } elseif (0 === --$sameHandlerLimit) { 667 $handler = null; 668 break; 669 } 670 } 671 foreach ($handlers as $h) { 672 set_exception_handler($h); 673 } 674 if (!$handler) { 675 return; 676 } 677 if ($handler !== $h) { 678 $handler[0]->setExceptionHandler($h); 679 } 680 $handler = $handler[0]; 681 $handlers = array(); 682 683 if ($exit = null === $error) { 684 $error = error_get_last(); 685 } 686 687 try { 688 while (self::$stackedErrorLevels) { 689 static::unstackErrors(); 690 } 691 } catch (\Exception $exception) { 692 // Handled below 693 } catch (\Throwable $exception) { 694 // Handled below 695 } 696 697 if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) { 698 // Let's not throw anymore but keep logging 699 $handler->throwAt(0, true); 700 $trace = isset($error['backtrace']) ? $error['backtrace'] : null; 701 702 if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { 703 $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace); 704 } else { 705 $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace); 706 } 707 } 708 709 try { 710 if (isset($exception)) { 711 self::$exitCode = 255; 712 $handler->handleException($exception, $error); 713 } 714 } catch (FatalErrorException $e) { 715 // Ignore this re-throw 716 } 717 718 if ($exit && self::$exitCode) { 719 $exitCode = self::$exitCode; 720 register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); }); 721 } 722 } 723 724 /** 725 * Configures the error handler for delayed handling. 726 * Ensures also that non-catchable fatal errors are never silenced. 727 * 728 * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 729 * PHP has a compile stage where it behaves unusually. To workaround it, 730 * we plug an error handler that only stacks errors for later. 731 * 732 * The most important feature of this is to prevent 733 * autoloading until unstackErrors() is called. 734 */ 735 public static function stackErrors() 736 { 737 self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); 738 } 739 740 /** 741 * Unstacks stacked errors and forwards to the logger. 742 */ 743 public static function unstackErrors() 744 { 745 $level = array_pop(self::$stackedErrorLevels); 746 747 if (null !== $level) { 748 $e = error_reporting($level); 749 if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) { 750 // If the user changed the error level, do not overwrite it 751 error_reporting($e); 752 } 753 } 754 755 if (empty(self::$stackedErrorLevels)) { 756 $errors = self::$stackedErrors; 757 self::$stackedErrors = array(); 758 759 foreach ($errors as $e) { 760 $e[0]->log($e[1], $e[2], $e[3]); 761 } 762 } 763 } 764 765 /** 766 * Gets the fatal error handlers. 767 * 768 * Override this method if you want to define more fatal error handlers. 769 * 770 * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface 771 */ 772 protected function getFatalErrorHandlers() 773 { 774 return array( 775 new UndefinedFunctionFatalErrorHandler(), 776 new UndefinedMethodFatalErrorHandler(), 777 new ClassNotFoundFatalErrorHandler(), 778 ); 779 } 780 781 /** 782 * Sets the level at which the conversion to Exception is done. 783 * 784 * @param int|null $level The level (null to use the error_reporting() value and 0 to disable) 785 * 786 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead. 787 */ 788 public function setLevel($level) 789 { 790 @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED); 791 792 $level = null === $level ? error_reporting() : $level; 793 $this->throwAt($level, true); 794 } 795 796 /** 797 * Sets the display_errors flag value. 798 * 799 * @param int $displayErrors The display_errors flag value 800 * 801 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead. 802 */ 803 public function setDisplayErrors($displayErrors) 804 { 805 @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED); 806 807 if ($displayErrors) { 808 $this->throwAt($this->displayErrors, true); 809 } else { 810 $displayErrors = $this->displayErrors; 811 $this->throwAt(0, true); 812 $this->displayErrors = $displayErrors; 813 } 814 } 815 816 /** 817 * Sets a logger for the given channel. 818 * 819 * @param LoggerInterface $logger A logger interface 820 * @param string $channel The channel associated with the logger (deprecation, emergency or scream) 821 * 822 * @deprecated since version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead. 823 */ 824 public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') 825 { 826 @trigger_error('The '.__METHOD__.' static method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the setLoggers() or setDefaultLogger() methods instead.', E_USER_DEPRECATED); 827 828 $handler = set_error_handler('var_dump'); 829 $handler = \is_array($handler) ? $handler[0] : null; 830 restore_error_handler(); 831 if (!$handler instanceof self) { 832 return; 833 } 834 if ('deprecation' === $channel) { 835 $handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true); 836 $handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED); 837 } elseif ('scream' === $channel) { 838 $handler->setDefaultLogger($logger, E_ALL | E_STRICT, false); 839 $handler->screamAt(E_ALL | E_STRICT); 840 } elseif ('emergency' === $channel) { 841 $handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true); 842 $handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); 843 } 844 } 845 846 /** 847 * @deprecated since version 2.6, to be removed in 3.0. Use handleError() instead. 848 */ 849 public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) 850 { 851 $this->handleError(E_USER_DEPRECATED, 'The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the handleError() method instead.', __FILE__, __LINE__, array()); 852 853 return $this->handleError($level, $message, $file, $line, (array) $context); 854 } 855 856 /** 857 * Handles PHP fatal errors. 858 * 859 * @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead. 860 */ 861 public function handleFatal() 862 { 863 @trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the handleFatalError() method instead.', E_USER_DEPRECATED); 864 865 static::handleFatalError(); 866 } 867 } 868 869 /** 870 * Private class used to work around https://bugs.php.net/54275. 871 * 872 * @author Nicolas Grekas <p@tchwork.com> 873 * 874 * @internal 875 */ 876 class ErrorHandlerCanary 877 { 878 private static $displayErrors = null; 879 880 public function __construct() 881 { 882 if (null === self::$displayErrors) { 883 self::$displayErrors = ini_set('display_errors', 1); 884 } 885 } 886 887 public function __destruct() 888 { 889 if (null !== self::$displayErrors) { 890 ini_set('display_errors', self::$displayErrors); 891 self::$displayErrors = null; 892 } 893 } 894 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Nov 11 20:33:01 2020 | Cross-referenced by PHPXref 0.7.1 |