4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Debug;
15 use Psr\Log\LoggerInterface;
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\UndefinedFunctionFatalErrorHandler;
21 use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
22 use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
23 use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
26 * A generic ErrorHandler for the PHP engine.
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
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.
45 * @author Nicolas Grekas <p@tchwork.com>
50 * @deprecated since version 2.6, to be removed in 3.0.
52 const TYPE_DEPRECATION = -100;
54 private $levels = array(
55 E_DEPRECATED => 'Deprecated',
56 E_USER_DEPRECATED => 'User Deprecated',
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',
69 E_CORE_ERROR => 'Core Error',
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),
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;
96 private $loggedTraces = array();
97 private $isRecursive = 0;
98 private $isRoot = false;
99 private $exceptionHandler;
100 private $bootstrappingLogger;
102 private static $reservedMemory;
103 private static $stackedErrors = array();
104 private static $stackedErrorLevels = array();
105 private static $toStringException = null;
108 * Same init value as thrownErrors.
110 * @deprecated since version 2.6, to be removed in 3.0.
112 private $displayErrors = 0x1FFF;
115 * Registers the error handler.
117 * @param self|null|int $handler The handler to register, or @deprecated (since version 2.6, to be removed in 3.0) bit field of thrown levels
118 * @param bool $replace Whether to replace or not any existing handler
120 * @return self The registered error handler
122 public static function register($handler = null, $replace = true)
124 if (null === self::$reservedMemory) {
125 self::$reservedMemory = str_repeat('x', 10240);
126 register_shutdown_function(__CLASS__.'::handleFatalError');
131 if ($handlerIsNew = !$handler instanceof self) {
132 // @deprecated polymorphism, to be removed in 3.0
133 if (null !== $handler) {
134 $levels = $replace ? $handler : 0;
137 $handler = new static();
140 if (null === $prev = set_error_handler(array($handler, 'handleError'))) {
141 restore_error_handler();
142 // Specifying the error types earlier would expose us to https://bugs.php.net/63206
143 set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors);
144 $handler->isRoot = true;
147 if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) {
151 if ($replace || !$prev) {
152 $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException')));
154 restore_error_handler();
157 $handler->throwAt($levels & $handler->thrownErrors, true);
162 public function __construct(BufferingLogger $bootstrappingLogger = null)
164 if ($bootstrappingLogger) {
165 $this->bootstrappingLogger = $bootstrappingLogger;
166 $this->setDefaultLogger($bootstrappingLogger);
171 * Sets a logger to non assigned errors levels.
173 * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels
174 * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
175 * @param bool $replace Whether to replace or not any existing logger
177 public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
181 if (is_array($levels)) {
182 foreach ($levels as $type => $logLevel) {
183 if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
184 $loggers[$type] = array($logger, $logLevel);
188 if (null === $levels) {
189 $levels = E_ALL | E_STRICT;
191 foreach ($this->loggers as $type => $log) {
192 if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
194 $loggers[$type] = $log;
199 $this->setLoggers($loggers);
203 * Sets a logger for each error level.
205 * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
207 * @return array The previous map
209 * @throws \InvalidArgumentException
211 public function setLoggers(array $loggers)
213 $prevLogged = $this->loggedErrors;
214 $prev = $this->loggers;
217 foreach ($loggers as $type => $log) {
218 if (!isset($prev[$type])) {
219 throw new \InvalidArgumentException('Unknown error type: '.$type);
221 if (!is_array($log)) {
223 } elseif (!array_key_exists(0, $log)) {
224 throw new \InvalidArgumentException('No logger provided');
226 if (null === $log[0]) {
227 $this->loggedErrors &= ~$type;
228 } elseif ($log[0] instanceof LoggerInterface) {
229 $this->loggedErrors |= $type;
231 throw new \InvalidArgumentException('Invalid logger provided');
233 $this->loggers[$type] = $log + $prev[$type];
235 if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
236 $flush[$type] = $type;
239 $this->reRegister($prevLogged | $this->thrownErrors);
242 foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
243 $type = $log[2]['type'];
244 if (!isset($flush[$type])) {
245 $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
246 } elseif ($this->loggers[$type][0]) {
247 $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
256 * Sets a user exception handler.
258 * @param callable $handler A handler that will be called on Exception
260 * @return callable|null The previous exception handler
262 * @throws \InvalidArgumentException
264 public function setExceptionHandler($handler)
266 if (null !== $handler && !is_callable($handler)) {
267 throw new \LogicException('The exception handler must be a valid PHP callable.');
269 $prev = $this->exceptionHandler;
270 $this->exceptionHandler = $handler;
276 * Sets the PHP error levels that throw an exception when a PHP error occurs.
278 * @param int $levels A bit field of E_* constants for thrown errors
279 * @param bool $replace Replace or amend the previous value
281 * @return int The previous value
283 public function throwAt($levels, $replace = false)
285 $prev = $this->thrownErrors;
286 $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
288 $this->thrownErrors |= $prev;
290 $this->reRegister($prev | $this->loggedErrors);
292 // $this->displayErrors is @deprecated since version 2.6
293 $this->displayErrors = $this->thrownErrors;
299 * Sets the PHP error levels for which local variables are preserved.
301 * @param int $levels A bit field of E_* constants for scoped errors
302 * @param bool $replace Replace or amend the previous value
304 * @return int The previous value
306 public function scopeAt($levels, $replace = false)
308 $prev = $this->scopedErrors;
309 $this->scopedErrors = (int) $levels;
311 $this->scopedErrors |= $prev;
318 * Sets the PHP error levels for which the stack trace is preserved.
320 * @param int $levels A bit field of E_* constants for traced errors
321 * @param bool $replace Replace or amend the previous value
323 * @return int The previous value
325 public function traceAt($levels, $replace = false)
327 $prev = $this->tracedErrors;
328 $this->tracedErrors = (int) $levels;
330 $this->tracedErrors |= $prev;
337 * Sets the error levels where the @-operator is ignored.
339 * @param int $levels A bit field of E_* constants for screamed errors
340 * @param bool $replace Replace or amend the previous value
342 * @return int The previous value
344 public function screamAt($levels, $replace = false)
346 $prev = $this->screamedErrors;
347 $this->screamedErrors = (int) $levels;
349 $this->screamedErrors |= $prev;
356 * Re-registers as a PHP error handler if levels changed.
358 private function reRegister($prev)
360 if ($prev !== $this->thrownErrors | $this->loggedErrors) {
361 $handler = set_error_handler('var_dump');
362 $handler = is_array($handler) ? $handler[0] : null;
363 restore_error_handler();
364 if ($handler === $this) {
365 restore_error_handler();
367 set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
369 set_error_handler(array($this, 'handleError'));
376 * Handles errors by filtering then logging them according to the configured bit fields.
378 * @param int $type One of the E_* constants
379 * @param string $message
380 * @param string $file
383 * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
385 * @throws \ErrorException When $this->thrownErrors requests so
389 public function handleError($type, $message, $file, $line)
391 $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
392 $log = $this->loggedErrors & $type;
393 $throw = $this->thrownErrors & $type & $level;
394 $type &= $level | $this->screamedErrors;
396 if (!$type || (!$log && !$throw)) {
397 return $type && $log;
399 $scope = $this->scopedErrors & $type;
401 if (4 < $numArgs = func_num_args()) {
402 $context = $scope ? (func_get_arg(4) ?: array()) : array();
403 $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
409 if (isset($context['GLOBALS']) && $scope) {
410 $e = $context; // Whatever the signature of the method,
411 unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
415 if (null !== $backtrace && $type & E_ERROR) {
416 // E_ERROR fatal errors are triggered on HHVM when
417 // hhvm.error_handling.call_user_handler_on_fatals=1
418 // which is the way to get their backtrace.
419 $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace'));
425 if (null !== self::$toStringException) {
426 $throw = self::$toStringException;
427 self::$toStringException = null;
428 } elseif ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
429 // Checking for class existence is a work around for https://bugs.php.net/42098
430 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
432 $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
435 if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) {
436 // Exceptions thrown from error handlers are sometimes not caught by the exception
437 // handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
438 // We temporarily re-enable display_errors to prevent any blank page related to this bug.
440 $throw->errorHandlerCanary = new ErrorHandlerCanary();
443 if (E_USER_ERROR & $type) {
444 $backtrace = $backtrace ?: $throw->getTrace();
446 for ($i = 1; isset($backtrace[$i]); ++$i) {
447 if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
448 && '__toString' === $backtrace[$i]['function']
449 && '->' === $backtrace[$i]['type']
450 && !isset($backtrace[$i - 1]['class'])
451 && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
453 // Here, we know trigger_error() has been called from __toString().
454 // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
455 // A small convention allows working around the limitation:
456 // given a caught $e exception in __toString(), quitting the method with
457 // `return trigger_error($e, E_USER_ERROR);` allows this error handler
458 // to make $e get through the __toString() barrier.
460 foreach ($context as $e) {
461 if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
467 self::$toStringException = $e;
474 // On PHP (not on HHVM), display the original error message instead of the default one.
475 $this->handleException($throw);
477 // Stop the process by giving back the error to the native handler.
487 // For duplicated errors, log the trace only once
488 $e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
491 if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
494 $this->loggedTraces[$e] = 1;
497 $e = compact('type', 'file', 'line', 'level');
499 if ($type & $level) {
501 $e['scope_vars'] = $context;
503 $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
506 if (null === $backtrace) {
507 $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
509 foreach ($backtrace as &$frame) {
510 unset($frame['args'], $frame);
512 $e['stack'] = $backtrace;
517 if ($this->isRecursive) {
519 } elseif (self::$stackedErrorLevels) {
520 self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
523 $this->isRecursive = true;
524 $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
525 $this->isRecursive = false;
526 } catch (\Exception $e) {
527 $this->isRecursive = false;
530 } catch (\Throwable $e) {
531 $this->isRecursive = false;
537 return $type && $log;
541 * Handles an exception by logging then forwarding it to another handler.
543 * @param \Exception|\Throwable $exception An exception to handle
544 * @param array $error An array as returned by error_get_last()
548 public function handleException($exception, array $error = null)
550 if (!$exception instanceof \Exception) {
551 $exception = new FatalThrowableError($exception);
553 $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
555 if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
558 'file' => $exception->getFile(),
559 'line' => $exception->getLine(),
560 'level' => error_reporting(),
561 'stack' => $exception->getTrace(),
563 if ($exception instanceof FatalErrorException) {
564 if ($exception instanceof FatalThrowableError) {
567 'message' => $message = $exception->getMessage(),
568 'file' => $e['file'],
569 'line' => $e['line'],
572 $message = 'Fatal '.$exception->getMessage();
574 } elseif ($exception instanceof \ErrorException) {
575 $message = 'Uncaught '.$exception->getMessage();
576 if ($exception instanceof ContextErrorException) {
577 $e['context'] = $exception->getContext();
580 $message = 'Uncaught Exception: '.$exception->getMessage();
583 if ($this->loggedErrors & $type) {
585 $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
586 } catch (\Exception $handlerException) {
587 } catch (\Throwable $handlerException) {
590 if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
591 foreach ($this->getFatalErrorHandlers() as $handler) {
592 if ($e = $handler->handleError($error, $exception)) {
598 if (empty($this->exceptionHandler)) {
599 throw $exception; // Give back $exception to the native handler
602 call_user_func($this->exceptionHandler, $exception);
603 } catch (\Exception $handlerException) {
604 } catch (\Throwable $handlerException) {
606 if (isset($handlerException)) {
607 $this->exceptionHandler = null;
608 $this->handleException($handlerException);
613 * Shutdown registered function for handling PHP fatal errors.
615 * @param array $error An array as returned by error_get_last()
619 public static function handleFatalError(array $error = null)
621 if (null === self::$reservedMemory) {
625 self::$reservedMemory = null;
627 $handler = set_error_handler('var_dump');
628 $handler = is_array($handler) ? $handler[0] : null;
629 restore_error_handler();
631 if (!$handler instanceof self) {
635 if (null === $error) {
636 $error = error_get_last();
640 while (self::$stackedErrorLevels) {
641 static::unstackErrors();
643 } catch (\Exception $exception) {
645 } catch (\Throwable $exception) {
649 if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
650 // Let's not throw anymore but keep logging
651 $handler->throwAt(0, true);
652 $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
654 if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
655 $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
657 $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
659 } elseif (!isset($exception)) {
664 $handler->handleException($exception, $error);
665 } catch (FatalErrorException $e) {
666 // Ignore this re-throw
671 * Configures the error handler for delayed handling.
672 * Ensures also that non-catchable fatal errors are never silenced.
674 * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
675 * PHP has a compile stage where it behaves unusually. To workaround it,
676 * we plug an error handler that only stacks errors for later.
678 * The most important feature of this is to prevent
679 * autoloading until unstackErrors() is called.
681 public static function stackErrors()
683 self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
687 * Unstacks stacked errors and forwards to the logger.
689 public static function unstackErrors()
691 $level = array_pop(self::$stackedErrorLevels);
693 if (null !== $level) {
694 $e = error_reporting($level);
695 if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
696 // If the user changed the error level, do not overwrite it
701 if (empty(self::$stackedErrorLevels)) {
702 $errors = self::$stackedErrors;
703 self::$stackedErrors = array();
705 foreach ($errors as $e) {
706 $e[0]->log($e[1], $e[2], $e[3]);
712 * Gets the fatal error handlers.
714 * Override this method if you want to define more fatal error handlers.
716 * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
718 protected function getFatalErrorHandlers()
721 new UndefinedFunctionFatalErrorHandler(),
722 new UndefinedMethodFatalErrorHandler(),
723 new ClassNotFoundFatalErrorHandler(),
728 * Sets the level at which the conversion to Exception is done.
730 * @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
732 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
734 public function setLevel($level)
736 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
738 $level = null === $level ? error_reporting() : $level;
739 $this->throwAt($level, true);
743 * Sets the display_errors flag value.
745 * @param int $displayErrors The display_errors flag value
747 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
749 public function setDisplayErrors($displayErrors)
751 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
753 if ($displayErrors) {
754 $this->throwAt($this->displayErrors, true);
756 $displayErrors = $this->displayErrors;
757 $this->throwAt(0, true);
758 $this->displayErrors = $displayErrors;
763 * Sets a logger for the given channel.
765 * @param LoggerInterface $logger A logger interface
766 * @param string $channel The channel associated with the logger (deprecation, emergency or scream)
768 * @deprecated since version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
770 public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
772 @trigger_error('The '.__METHOD__.' static method is deprecated since version 2.6 and will be removed in 3.0. Use the setLoggers() or setDefaultLogger() methods instead.', E_USER_DEPRECATED);
774 $handler = set_error_handler('var_dump');
775 $handler = is_array($handler) ? $handler[0] : null;
776 restore_error_handler();
777 if (!$handler instanceof self) {
780 if ('deprecation' === $channel) {
781 $handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true);
782 $handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED);
783 } elseif ('scream' === $channel) {
784 $handler->setDefaultLogger($logger, E_ALL | E_STRICT, false);
785 $handler->screamAt(E_ALL | E_STRICT);
786 } elseif ('emergency' === $channel) {
787 $handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true);
788 $handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
793 * @deprecated since version 2.6, to be removed in 3.0. Use handleError() instead.
795 public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
797 $this->handleError(E_USER_DEPRECATED, 'The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleError() method instead.', __FILE__, __LINE__, array());
799 return $this->handleError($level, $message, $file, $line, (array) $context);
803 * Handles PHP fatal errors.
805 * @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead.
807 public function handleFatal()
809 @trigger_error('The '.__METHOD__.' method is deprecated since version 2.6 and will be removed in 3.0. Use the handleFatalError() method instead.', E_USER_DEPRECATED);
811 static::handleFatalError();
816 * Private class used to work around https://bugs.php.net/54275.
818 * @author Nicolas Grekas <p@tchwork.com>
822 class ErrorHandlerCanary
824 private static $displayErrors = null;
826 public function __construct()
828 if (null === self::$displayErrors) {
829 self::$displayErrors = ini_set('display_errors', 1);
833 public function __destruct()
835 if (null !== self::$displayErrors) {
836 ini_set('display_errors', self::$displayErrors);
837 self::$displayErrors = null;