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;
106 private static $exitCode = 0;
109 * Same init value as thrownErrors.
111 * @deprecated since version 2.6, to be removed in 3.0.
113 private $displayErrors = 0x1FFF;
116 * Registers the error handler.
118 * @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
119 * @param bool $replace Whether to replace or not any existing handler
121 * @return self The registered error handler
123 public static function register($handler = null, $replace = true)
125 if (null === self::$reservedMemory) {
126 self::$reservedMemory = str_repeat('x', 10240);
127 register_shutdown_function(__CLASS__.'::handleFatalError');
132 if ($handlerIsNew = !$handler instanceof self) {
133 // @deprecated polymorphism, to be removed in 3.0
134 if (null !== $handler) {
135 $levels = $replace ? $handler : 0;
138 $handler = new static();
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;
148 if ($handlerIsNew && is_array($prev) && $prev[0] instanceof self) {
152 if ($replace || !$prev) {
153 $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException')));
155 restore_error_handler();
158 $handler->throwAt($levels & $handler->thrownErrors, true);
163 public function __construct(BufferingLogger $bootstrappingLogger = null)
165 if ($bootstrappingLogger) {
166 $this->bootstrappingLogger = $bootstrappingLogger;
167 $this->setDefaultLogger($bootstrappingLogger);
172 * Sets a logger to non assigned errors levels.
174 * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels
175 * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants
176 * @param bool $replace Whether to replace or not any existing logger
178 public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
182 if (is_array($levels)) {
183 foreach ($levels as $type => $logLevel) {
184 if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
185 $loggers[$type] = array($logger, $logLevel);
189 if (null === $levels) {
190 $levels = E_ALL | E_STRICT;
192 foreach ($this->loggers as $type => $log) {
193 if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
195 $loggers[$type] = $log;
200 $this->setLoggers($loggers);
204 * Sets a logger for each error level.
206 * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map
208 * @return array The previous map
210 * @throws \InvalidArgumentException
212 public function setLoggers(array $loggers)
214 $prevLogged = $this->loggedErrors;
215 $prev = $this->loggers;
218 foreach ($loggers as $type => $log) {
219 if (!isset($prev[$type])) {
220 throw new \InvalidArgumentException('Unknown error type: '.$type);
222 if (!is_array($log)) {
224 } elseif (!array_key_exists(0, $log)) {
225 throw new \InvalidArgumentException('No logger provided');
227 if (null === $log[0]) {
228 $this->loggedErrors &= ~$type;
229 } elseif ($log[0] instanceof LoggerInterface) {
230 $this->loggedErrors |= $type;
232 throw new \InvalidArgumentException('Invalid logger provided');
234 $this->loggers[$type] = $log + $prev[$type];
236 if ($this->bootstrappingLogger && $prev[$type][0] === $this->bootstrappingLogger) {
237 $flush[$type] = $type;
240 $this->reRegister($prevLogged | $this->thrownErrors);
243 foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
244 $type = $log[2]['type'];
245 if (!isset($flush[$type])) {
246 $this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
247 } elseif ($this->loggers[$type][0]) {
248 $this->loggers[$type][0]->log($this->loggers[$type][1], $log[1], $log[2]);
257 * Sets a user exception handler.
259 * @param callable $handler A handler that will be called on Exception
261 * @return callable|null The previous exception handler
263 * @throws \InvalidArgumentException
265 public function setExceptionHandler($handler)
267 if (null !== $handler && !is_callable($handler)) {
268 throw new \LogicException('The exception handler must be a valid PHP callable.');
270 $prev = $this->exceptionHandler;
271 $this->exceptionHandler = $handler;
277 * Sets the PHP error levels that throw an exception when a PHP error occurs.
279 * @param int $levels A bit field of E_* constants for thrown errors
280 * @param bool $replace Replace or amend the previous value
282 * @return int The previous value
284 public function throwAt($levels, $replace = false)
286 $prev = $this->thrownErrors;
287 $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED;
289 $this->thrownErrors |= $prev;
291 $this->reRegister($prev | $this->loggedErrors);
293 // $this->displayErrors is @deprecated since version 2.6
294 $this->displayErrors = $this->thrownErrors;
300 * Sets the PHP error levels for which local variables are preserved.
302 * @param int $levels A bit field of E_* constants for scoped errors
303 * @param bool $replace Replace or amend the previous value
305 * @return int The previous value
307 public function scopeAt($levels, $replace = false)
309 $prev = $this->scopedErrors;
310 $this->scopedErrors = (int) $levels;
312 $this->scopedErrors |= $prev;
319 * Sets the PHP error levels for which the stack trace is preserved.
321 * @param int $levels A bit field of E_* constants for traced errors
322 * @param bool $replace Replace or amend the previous value
324 * @return int The previous value
326 public function traceAt($levels, $replace = false)
328 $prev = $this->tracedErrors;
329 $this->tracedErrors = (int) $levels;
331 $this->tracedErrors |= $prev;
338 * Sets the error levels where the @-operator is ignored.
340 * @param int $levels A bit field of E_* constants for screamed errors
341 * @param bool $replace Replace or amend the previous value
343 * @return int The previous value
345 public function screamAt($levels, $replace = false)
347 $prev = $this->screamedErrors;
348 $this->screamedErrors = (int) $levels;
350 $this->screamedErrors |= $prev;
357 * Re-registers as a PHP error handler if levels changed.
359 private function reRegister($prev)
361 if ($prev !== $this->thrownErrors | $this->loggedErrors) {
362 $handler = set_error_handler('var_dump');
363 $handler = is_array($handler) ? $handler[0] : null;
364 restore_error_handler();
365 if ($handler === $this) {
366 restore_error_handler();
368 set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
370 set_error_handler(array($this, 'handleError'));
377 * Handles errors by filtering then logging them according to the configured bit fields.
379 * @param int $type One of the E_* constants
380 * @param string $message
381 * @param string $file
384 * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself
386 * @throws \ErrorException When $this->thrownErrors requests so
390 public function handleError($type, $message, $file, $line)
392 $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
393 $log = $this->loggedErrors & $type;
394 $throw = $this->thrownErrors & $type & $level;
395 $type &= $level | $this->screamedErrors;
397 if (!$type || (!$log && !$throw)) {
398 return $type && $log;
400 $scope = $this->scopedErrors & $type;
402 if (4 < $numArgs = func_num_args()) {
403 $context = $scope ? (func_get_arg(4) ?: array()) : array();
404 $backtrace = 5 < $numArgs ? func_get_arg(5) : null; // defined on HHVM
410 if (isset($context['GLOBALS']) && $scope) {
411 $e = $context; // Whatever the signature of the method,
412 unset($e['GLOBALS'], $context); // $context is always a reference in 5.3
416 if (null !== $backtrace && $type & E_ERROR) {
417 // E_ERROR fatal errors are triggered on HHVM when
418 // hhvm.error_handling.call_user_handler_on_fatals=1
419 // which is the way to get their backtrace.
420 $this->handleFatalError(compact('type', 'message', 'file', 'line', 'backtrace'));
426 if (null !== self::$toStringException) {
427 $throw = self::$toStringException;
428 self::$toStringException = null;
429 } elseif ($scope && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) {
430 // Checking for class existence is a work around for https://bugs.php.net/42098
431 $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context);
433 $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
436 if (\PHP_VERSION_ID <= 50407 && (\PHP_VERSION_ID >= 50400 || \PHP_VERSION_ID <= 50317)) {
437 // Exceptions thrown from error handlers are sometimes not caught by the exception
438 // handler and shutdown handlers are bypassed before 5.4.8/5.3.18.
439 // We temporarily re-enable display_errors to prevent any blank page related to this bug.
441 $throw->errorHandlerCanary = new ErrorHandlerCanary();
444 if (E_USER_ERROR & $type) {
445 $backtrace = $backtrace ?: $throw->getTrace();
447 for ($i = 1; isset($backtrace[$i]); ++$i) {
448 if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
449 && '__toString' === $backtrace[$i]['function']
450 && '->' === $backtrace[$i]['type']
451 && !isset($backtrace[$i - 1]['class'])
452 && ('trigger_error' === $backtrace[$i - 1]['function'] || 'user_error' === $backtrace[$i - 1]['function'])
454 // Here, we know trigger_error() has been called from __toString().
455 // HHVM is fine with throwing from __toString() but PHP triggers a fatal error instead.
456 // A small convention allows working around the limitation:
457 // given a caught $e exception in __toString(), quitting the method with
458 // `return trigger_error($e, E_USER_ERROR);` allows this error handler
459 // to make $e get through the __toString() barrier.
461 foreach ($context as $e) {
462 if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
468 self::$toStringException = $e;
475 // On PHP (not on HHVM), display the original error message instead of the default one.
476 $this->handleException($throw);
478 // Stop the process by giving back the error to the native handler.
488 // For duplicated errors, log the trace only once
489 $e = md5("{$type}/{$line}/{$file}\x00{$message}", true);
492 if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) {
495 $this->loggedTraces[$e] = 1;
498 $e = compact('type', 'file', 'line', 'level');
500 if ($type & $level) {
502 $e['scope_vars'] = $context;
504 $e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
507 if (null === $backtrace) {
508 $e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
510 foreach ($backtrace as &$frame) {
511 unset($frame['args'], $frame);
513 $e['stack'] = $backtrace;
518 if ($this->isRecursive) {
520 } elseif (self::$stackedErrorLevels) {
521 self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
524 $this->isRecursive = true;
525 $this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
526 $this->isRecursive = false;
527 } catch (\Exception $e) {
528 $this->isRecursive = false;
531 } catch (\Throwable $e) {
532 $this->isRecursive = false;
538 return $type && $log;
542 * Handles an exception by logging then forwarding it to another handler.
544 * @param \Exception|\Throwable $exception An exception to handle
545 * @param array $error An array as returned by error_get_last()
549 public function handleException($exception, array $error = null)
551 if (null === $error) {
552 self::$exitCode = 255;
554 if (!$exception instanceof \Exception) {
555 $exception = new FatalThrowableError($exception);
557 $type = $exception instanceof FatalErrorException ? $exception->getSeverity() : E_ERROR;
559 if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
562 'file' => $exception->getFile(),
563 'line' => $exception->getLine(),
564 'level' => error_reporting(),
565 'stack' => $exception->getTrace(),
567 if ($exception instanceof FatalErrorException) {
568 if ($exception instanceof FatalThrowableError) {
571 'message' => $message = $exception->getMessage(),
572 'file' => $e['file'],
573 'line' => $e['line'],
576 $message = 'Fatal '.$exception->getMessage();
578 } elseif ($exception instanceof \ErrorException) {
579 $message = 'Uncaught '.$exception->getMessage();
580 if ($exception instanceof ContextErrorException) {
581 $e['context'] = $exception->getContext();
584 $message = 'Uncaught Exception: '.$exception->getMessage();
587 if ($this->loggedErrors & $type) {
589 $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
590 } catch (\Exception $handlerException) {
591 } catch (\Throwable $handlerException) {
594 if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) {
595 foreach ($this->getFatalErrorHandlers() as $handler) {
596 if ($e = $handler->handleError($error, $exception)) {
602 if (empty($this->exceptionHandler)) {
603 throw $exception; // Give back $exception to the native handler
606 call_user_func($this->exceptionHandler, $exception);
607 } catch (\Exception $handlerException) {
608 } catch (\Throwable $handlerException) {
610 if (isset($handlerException)) {
611 $this->exceptionHandler = null;
612 $this->handleException($handlerException);
617 * Shutdown registered function for handling PHP fatal errors.
619 * @param array $error An array as returned by error_get_last()
623 public static function handleFatalError(array $error = null)
625 if (null === self::$reservedMemory) {
629 self::$reservedMemory = null;
631 $handler = set_error_handler('var_dump');
632 $handler = is_array($handler) ? $handler[0] : null;
633 restore_error_handler();
635 if (!$handler instanceof self) {
639 if ($exit = null === $error) {
640 $error = error_get_last();
644 while (self::$stackedErrorLevels) {
645 static::unstackErrors();
647 } catch (\Exception $exception) {
649 } catch (\Throwable $exception) {
653 if ($error && $error['type'] &= E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR) {
654 // Let's not throw anymore but keep logging
655 $handler->throwAt(0, true);
656 $trace = isset($error['backtrace']) ? $error['backtrace'] : null;
658 if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) {
659 $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false, $trace);
661 $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true, $trace);
666 if (isset($exception)) {
667 self::$exitCode = 255;
668 $handler->handleException($exception, $error);
670 } catch (FatalErrorException $e) {
671 // Ignore this re-throw
674 if ($exit && self::$exitCode) {
675 $exitCode = self::$exitCode;
676 register_shutdown_function('register_shutdown_function', function () use ($exitCode) { exit($exitCode); });
681 * Configures the error handler for delayed handling.
682 * Ensures also that non-catchable fatal errors are never silenced.
684 * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724
685 * PHP has a compile stage where it behaves unusually. To workaround it,
686 * we plug an error handler that only stacks errors for later.
688 * The most important feature of this is to prevent
689 * autoloading until unstackErrors() is called.
691 public static function stackErrors()
693 self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
697 * Unstacks stacked errors and forwards to the logger.
699 public static function unstackErrors()
701 $level = array_pop(self::$stackedErrorLevels);
703 if (null !== $level) {
704 $e = error_reporting($level);
705 if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
706 // If the user changed the error level, do not overwrite it
711 if (empty(self::$stackedErrorLevels)) {
712 $errors = self::$stackedErrors;
713 self::$stackedErrors = array();
715 foreach ($errors as $e) {
716 $e[0]->log($e[1], $e[2], $e[3]);
722 * Gets the fatal error handlers.
724 * Override this method if you want to define more fatal error handlers.
726 * @return FatalErrorHandlerInterface[] An array of FatalErrorHandlerInterface
728 protected function getFatalErrorHandlers()
731 new UndefinedFunctionFatalErrorHandler(),
732 new UndefinedMethodFatalErrorHandler(),
733 new ClassNotFoundFatalErrorHandler(),
738 * Sets the level at which the conversion to Exception is done.
740 * @param int|null $level The level (null to use the error_reporting() value and 0 to disable)
742 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
744 public function setLevel($level)
746 @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);
748 $level = null === $level ? error_reporting() : $level;
749 $this->throwAt($level, true);
753 * Sets the display_errors flag value.
755 * @param int $displayErrors The display_errors flag value
757 * @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
759 public function setDisplayErrors($displayErrors)
761 @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);
763 if ($displayErrors) {
764 $this->throwAt($this->displayErrors, true);
766 $displayErrors = $this->displayErrors;
767 $this->throwAt(0, true);
768 $this->displayErrors = $displayErrors;
773 * Sets a logger for the given channel.
775 * @param LoggerInterface $logger A logger interface
776 * @param string $channel The channel associated with the logger (deprecation, emergency or scream)
778 * @deprecated since version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
780 public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
782 @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);
784 $handler = set_error_handler('var_dump');
785 $handler = is_array($handler) ? $handler[0] : null;
786 restore_error_handler();
787 if (!$handler instanceof self) {
790 if ('deprecation' === $channel) {
791 $handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true);
792 $handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED);
793 } elseif ('scream' === $channel) {
794 $handler->setDefaultLogger($logger, E_ALL | E_STRICT, false);
795 $handler->screamAt(E_ALL | E_STRICT);
796 } elseif ('emergency' === $channel) {
797 $handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true);
798 $handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
803 * @deprecated since version 2.6, to be removed in 3.0. Use handleError() instead.
805 public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
807 $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());
809 return $this->handleError($level, $message, $file, $line, (array) $context);
813 * Handles PHP fatal errors.
815 * @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead.
817 public function handleFatal()
819 @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);
821 static::handleFatalError();
826 * Private class used to work around https://bugs.php.net/54275.
828 * @author Nicolas Grekas <p@tchwork.com>
832 class ErrorHandlerCanary
834 private static $displayErrors = null;
836 public function __construct()
838 if (null === self::$displayErrors) {
839 self::$displayErrors = ini_set('display_errors', 1);
843 public function __destruct()
845 if (null !== self::$displayErrors) {
846 ini_set('display_errors', self::$displayErrors);
847 self::$displayErrors = null;