diff --git a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig index e2d7d5faae..4ab569020f 100644 --- a/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig +++ b/src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/logger.html.twig @@ -105,7 +105,7 @@ {% macro display_message(log_index, log) %} - {% if constant('Symfony\\Component\\HttpKernel\\Debug\\ErrorHandler::TYPE_DEPRECATION') == log.context.type|default(0) %} + {% if log.context.level is defined and log.context.type is defined and (constant('E_DEPRECATED') == log.context.type or constant('E_USER_DEPRECATED') == log.context.type) %} DEPRECATION - {{ log.message }} {% set id = 'sf-call-stack-' ~ log_index %} diff --git a/src/Symfony/Component/Debug/CHANGELOG.md b/src/Symfony/Component/Debug/CHANGELOG.md index c3348aa4da..ff7c6af2b7 100644 --- a/src/Symfony/Component/Debug/CHANGELOG.md +++ b/src/Symfony/Component/Debug/CHANGELOG.md @@ -4,6 +4,8 @@ CHANGELOG 2.6.0 ----- +* generalized ErrorHandler and ExceptionHandler, + with some new methods and others deprecated * enhanced error messages for uncaught exceptions 2.5.0 diff --git a/src/Symfony/Component/Debug/Debug.php b/src/Symfony/Component/Debug/Debug.php index e0710c59e6..fd308e8003 100644 --- a/src/Symfony/Component/Debug/Debug.php +++ b/src/Symfony/Component/Debug/Debug.php @@ -39,15 +39,23 @@ class Debug static::$enabled = true; - error_reporting(-1); + if (null !== $errorReportingLevel) { + error_reporting($errorReportingLevel); + } else { + error_reporting(-1); + } - ErrorHandler::register($errorReportingLevel, $displayErrors); if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); ExceptionHandler::register(); - // CLI - display errors only if they're not already logged to STDERR } elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) { + // CLI - display errors only if they're not already logged to STDERR ini_set('display_errors', 1); } + $handler = ErrorHandler::register(); + if (!$displayErrors) { + $handler->throwAt(0, true); + } DebugClassLoader::enable(); } diff --git a/src/Symfony/Component/Debug/DebugClassLoader.php b/src/Symfony/Component/Debug/DebugClassLoader.php index 4a02e616cc..53003aa2f5 100644 --- a/src/Symfony/Component/Debug/DebugClassLoader.php +++ b/src/Symfony/Component/Debug/DebugClassLoader.php @@ -78,7 +78,7 @@ class DebugClassLoader public static function enable() { // Ensures we don't hit https://bugs.php.net/42098 - class_exists(__NAMESPACE__.'\ErrorHandler', true); + class_exists('Symfony\Component\Debug\ErrorHandler'); if (!is_array($functions = spl_autoload_functions())) { return; diff --git a/src/Symfony/Component/Debug/ErrorHandler.php b/src/Symfony/Component/Debug/ErrorHandler.php index 152994b75d..5982b0c482 100644 --- a/src/Symfony/Component/Debug/ErrorHandler.php +++ b/src/Symfony/Component/Debug/ErrorHandler.php @@ -22,188 +22,492 @@ use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler; use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface; /** - * ErrorHandler. + * A generic ErrorHandler for the PHP engine. + * + * Provides five bit fields that control how errors are handled: + * - thrownErrors: errors thrown as \ErrorException + * - loggedErrors: logged errors, when not @-silenced + * - scopedErrors: errors thrown or logged with their local context + * - tracedErrors: errors logged with their stack trace, only once for repeated errors + * - screamedErrors: never @-silenced errors + * + * Each error level can be logged by a dedicated PSR-3 logger object. + * Screaming only applies to logging. + * Throwing takes precedence over logging. + * Uncaught exceptions are logged as E_ERROR. + * E_DEPRECATED and E_USER_DEPRECATED levels never throw. + * E_RECOVERABLE_ERROR and E_USER_ERROR levels always throw. + * Non catchable errors that can be detected at shutdown time are logged when the scream bit field allows so. + * As errors have a performance cost, repeated errors are all logged, so that the developer + * can see them and weight them as more important to fix than others of the same level. * - * @author Fabien Potencier - * @author Konstantin Myakshin * @author Nicolas Grekas */ class ErrorHandler { + /** + * @deprecated since 2.6, to be removed in 3.0. + */ const TYPE_DEPRECATION = -100; private $levels = array( - E_WARNING => 'Warning', - E_NOTICE => 'Notice', - E_USER_ERROR => 'User Error', - E_USER_WARNING => 'User Warning', - E_USER_NOTICE => 'User Notice', - E_STRICT => 'Runtime Notice', - E_RECOVERABLE_ERROR => 'Catchable Fatal Error', E_DEPRECATED => 'Deprecated', E_USER_DEPRECATED => 'User Deprecated', - E_ERROR => 'Error', - E_CORE_ERROR => 'Core Error', + E_NOTICE => 'Notice', + E_USER_NOTICE => 'User Notice', + E_STRICT => 'Runtime Notice', + E_WARNING => 'Warning', + E_USER_WARNING => 'User Warning', + E_COMPILE_WARNING => 'Compile Warning', + E_CORE_WARNING => 'Core Warning', + E_USER_ERROR => 'User Error', + E_RECOVERABLE_ERROR => 'Catchable Fatal Error', E_COMPILE_ERROR => 'Compile Error', E_PARSE => 'Parse Error', + E_ERROR => 'Error', + E_CORE_ERROR => 'Core Error', ); - private $level; + private $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array(null, LogLevel::NOTICE), + E_USER_NOTICE => array(null, LogLevel::NOTICE), + E_STRICT => array(null, LogLevel::NOTICE), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::ERROR), + E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR), + E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY), + E_PARSE => array(null, LogLevel::EMERGENCY), + E_ERROR => array(null, LogLevel::EMERGENCY), + E_CORE_ERROR => array(null, LogLevel::EMERGENCY), + ); - private $reservedMemory; + private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED + private $tracedErrors = 0x77FB; // E_ALL - E_STRICT - E_PARSE + private $screamedErrors = 0x55; // E_ERROR + E_CORE_ERROR + E_COMPILE_ERROR + E_PARSE + private $loggedErrors = 0; - private $displayErrors; + private $loggedTraces = array(); + private $isRecursive = 0; + private $exceptionHandler; + + private static $reservedMemory; + private static $stackedErrors = array(); + private static $stackedErrorLevels = array(); /** - * @var LoggerInterface[] Loggers for channels + * Same init value as thrownErrors + * + * @deprecated since 2.6, to be removed in 3.0. */ - private static $loggers = array(); - - private static $stackedErrors = array(); - - private static $stackedErrorLevels = array(); + private $displayErrors = 0x1FFF; /** * Registers the error handler. * - * @param int $level The level at which the conversion to Exception is done (null to use the error_reporting() value and 0 to disable) - * @param bool $displayErrors Display errors (for dev environment) or just log them (production usage) + * @param int $levels Levels to register to for throwing, 0 for none + * @param bool $throw @deprecated argument, same as setting $levels to 0 * * @return ErrorHandler The registered error handler */ - public static function register($level = null, $displayErrors = true) + public static function register($levels = -1, $throw = true) { - $handler = new static(); - $handler->setLevel($level); - $handler->setDisplayErrors($displayErrors); + if (null === self::$reservedMemory) { + self::$reservedMemory = str_repeat('x', 10240); + register_shutdown_function(__CLASS__.'::handleFatalError'); + } - ini_set('display_errors', 0); - set_error_handler(array($handler, 'handle')); - register_shutdown_function(array($handler, 'handleFatal')); - $handler->reservedMemory = str_repeat('x', 10240); + $handler = new static(); + $levels &= $handler->thrownErrors; + set_error_handler(array($handler, 'handleError'), $levels); + $handler->throwAt($throw ? $levels : 0, true); + $handler->setExceptionHandler(set_exception_handler(array($handler, 'handleException'))); return $handler; } /** - * Sets the level at which the conversion to Exception is done. + * Sets a logger to non assigned errors levels. * - * @param int|null $level The level (null to use the error_reporting() value and 0 to disable) + * @param LoggerInterface $logger A PSR-3 logger to put as default for the given levels + * @param array|int $levels An array map of E_* to LogLevel::* or an integer bit field of E_* constants + * @param bool $replace Whether to replace or not any existing logger */ - public function setLevel($level) + public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false) { - $this->level = null === $level ? error_reporting() : $level; + $loggers = array(); + + if (is_array($levels)) { + foreach ($levels as $type => $logLevel) { + if (empty($this->loggers[$type][0]) || $replace) { + $loggers[$type] = array($logger, $logLevel); + } + } + } else { + if (null === $levels) { + $levels = E_ALL | E_STRICT; + } + foreach ($this->loggers as $type => $log) { + if (($type & $levels) && (empty($log[0]) || $replace)) { + $log[0] = $logger; + $loggers[$type] = $log; + } + } + } + + $this->setLoggers($loggers); } /** - * Sets the display_errors flag value. + * Sets a logger for each error level. * - * @param int $displayErrors The display_errors flag value + * @param array $loggers Error levels to [LoggerInterface|null, LogLevel::*] map + * + * @return array The previous map + * + * @throws \InvalidArgumentException */ - public function setDisplayErrors($displayErrors) + public function setLoggers(array $loggers) { - $this->displayErrors = $displayErrors; + $prevLogged = $this->loggedErrors; + $prev = $this->loggers; + + foreach ($loggers as $type => $log) { + if (!isset($prev[$type])) { + throw new \InvalidArgumentException('Unknown error type: '.$type); + } + if (!is_array($log)) { + $log = array($log); + } elseif (!array_key_exists(0, $log)) { + throw new \InvalidArgumentException('No logger provided'); + } + if (null === $log[0]) { + $this->loggedErrors &= ~$type; + } elseif ($log[0] instanceof LoggerInterface) { + $this->loggedErrors |= $type; + } else { + throw new \InvalidArgumentException('Invalid logger provided'); + } + $this->loggers[$type] = $log + $prev[$type]; + } + $this->reRegister($prevLogged | $this->thrownErrors); + + return $prev; } /** - * Sets a logger for the given channel. + * Sets a user exception handler. * - * @param LoggerInterface $logger A logger interface - * @param string $channel The channel associated with the logger (deprecation, emergency or scream) + * @param callable $handler A handler that will be called on Exception + * + * @return callable|null The previous exception handler + * + * @throws \InvalidArgumentException */ - public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') + public function setExceptionHandler($handler) { - self::$loggers[$channel] = $logger; + if (null !== $handler && !is_callable($handler)) { + throw new \LogicException('The exception handler must be a valid PHP callable.'); + } + $prev = $this->exceptionHandler; + $this->exceptionHandler = $handler; + + return $prev; } /** - * @throws \ErrorException When error_reporting returns error + * Sets the error levels that are to be thrown. + * + * @param int $levels A bit field of E_* constants for thrown errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value */ - public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) + public function throwAt($levels, $replace = false) { - if ($level & (E_USER_DEPRECATED | E_DEPRECATED)) { - if (isset(self::$loggers['deprecation'])) { - if (self::$stackedErrorLevels) { - self::$stackedErrors[] = func_get_args(); + $prev = $this->thrownErrors; + $this->thrownErrors = ($levels | E_RECOVERABLE_ERROR | E_USER_ERROR) & ~E_USER_DEPRECATED & ~E_DEPRECATED; + if (!$replace) { + $this->thrownErrors |= $prev; + } + $this->reRegister($prev | $this->loggedErrors); + + // $this->displayErrors is @deprecated since 2.6 + $this->displayErrors = $this->thrownErrors; + + return $prev; + } + + /** + * Sets the error levels that are logged or thrown with their local scope. + * + * @param int $levels A bit field of E_* constants for scoped errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function scopeAt($levels, $replace = false) + { + $prev = $this->scopedErrors; + $this->scopedErrors = (int) $levels; + if (!$replace) { + $this->scopedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the error levels that are logged with their stack trace. + * + * @param int $levels A bit field of E_* constants for traced errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function traceAt($levels, $replace = false) + { + $prev = $this->tracedErrors; + $this->tracedErrors = (int) $levels; + if (!$replace) { + $this->tracedErrors |= $prev; + } + + return $prev; + } + + /** + * Sets the error levels where the @-operator is ignored. + * + * @param int $levels A bit field of E_* constants for screamed errors + * @param bool $replace Replace or amend the previous value + * + * @return int The previous value + */ + public function screamAt($levels, $replace = false) + { + $prev = $this->screamedErrors; + $this->screamedErrors = (int) $levels; + if (!$replace) { + $this->screamedErrors |= $prev; + } + + return $prev; + } + + /** + * Re-registers as a PHP error handler if levels changed. + */ + private function reRegister($prev) + { + if ($prev !== $this->thrownErrors | $this->loggedErrors) { + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler === $this) { + restore_error_handler(); + set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors); + } + } + } + + /** + * Handles errors by filtering then logging them according to the configured bit fields. + * + * @param int $type One of the E_* constants + * @param string $file + * @param int $line + * @param array $context + * + * @return bool Returns false when no handling happens so that the PHP engine can handle the error itself. + * + * @throws \ErrorException When $this->thrownErrors requests so + * + * @internal + */ + public function handleError($type, $message, $file, $line, array $context) + { + $level = error_reporting() | E_RECOVERABLE_ERROR | E_USER_ERROR; + $log = $this->loggedErrors & $type; + $throw = $this->thrownErrors & $type & $level; + $type &= $level | $this->screamedErrors; + + if ($type && ($log || $throw)) { + if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && ($this->scopedErrors & $type)) { + $e = $context; // Whatever the signature of the method, + unset($e['GLOBALS'], $context); // $context is always a reference in 5.3 + $context = $e; + } + + if ($throw) { + if (($this->scopedErrors & $type) && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { + // Checking for class existence is a work around for https://bugs.php.net/42098 + $throw = new ContextErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line, $context); } else { - if (version_compare(PHP_VERSION, '5.4', '<')) { - $stack = array_map( - function ($row) { - unset($row['args']); + $throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line); + } - return $row; - }, - array_slice(debug_backtrace(false), 0, 10) - ); - } else { - $stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10); + if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) { + // Exceptions thrown from error handlers are sometimes not caught by the exception + // handler and shutdown handlers are bypassed before 5.4.8/5.3.18. + // We temporarily re-enable display_errors to prevent any blank page related to this bug. + + $throw->errorHandlerCanary = new ErrorHandlerCanary(); + } + + throw $throw; + } + + // For duplicated errors, log the trace only once + $e = md5("{$type}/{$line}/{$file}\x00{$message}", true); + $trace = true; + + if (!($this->tracedErrors & $type) || isset($this->loggedTraces[$e])) { + $trace = false; + } else { + $this->loggedTraces[$e] = 1; + } + + $e = compact('type', 'file', 'line', 'level'); + + if ($type & $level) { + if ($this->scopedErrors & $type) { + $e['context'] = $context; + if ($trace) { + $e['stack'] = debug_backtrace(true); // Provide object } - - self::$loggers['deprecation']->warning($message, array('type' => self::TYPE_DEPRECATION, 'stack' => $stack)); + } elseif ($trace) { + $e['stack'] = debug_backtrace(PHP_VERSION_ID >= 50306 ? DEBUG_BACKTRACE_IGNORE_ARGS : false); } - - return true; - } - } elseif ($this->displayErrors && error_reporting() & $level && $this->level & $level) { - if (PHP_VERSION_ID < 50400 && isset($context['GLOBALS']) && is_array($context)) { - $c = $context; // Whatever the signature of the method, - unset($c['GLOBALS'], $context); // $context is always a reference in 5.3 - $context = $c; } - $exception = sprintf('%s: %s', isset($this->levels[$level]) ? $this->levels[$level] : $level, $message); - if ($context && class_exists('Symfony\Component\Debug\Exception\ContextErrorException')) { - // Checking for class existence is a work around for https://bugs.php.net/42098 - $exception = new ContextErrorException($exception, 0, $level, $file, $line, $context); + if ($this->isRecursive) { + $log = 0; + } elseif (self::$stackedErrorLevels) { + self::$stackedErrors[] = array($this->loggers[$type], $message, $e); } else { - $exception = new \ErrorException($exception, 0, $level, $file, $line); - } + try { + $this->isRecursive = true; + $this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e); + $this->isRecursive = false; + } catch (\Exception $e) { + $this->isRecursive = false; - if (PHP_VERSION_ID <= 50407 && (PHP_VERSION_ID >= 50400 || PHP_VERSION_ID <= 50317)) { - // Exceptions thrown from error handlers are sometimes not caught by the exception - // handler and shutdown handlers are bypassed before 5.4.8/5.3.18. - // We temporarily re-enable display_errors to prevent any blank page related to this bug. - - $exception->errorHandlerCanary = new ErrorHandlerCanary(); - } - - throw $exception; - } - - if (isset(self::$loggers['scream']) && !(error_reporting() & $level)) { - if (self::$stackedErrorLevels) { - self::$stackedErrors[] = func_get_args(); - } else { - switch ($level) { - case E_USER_ERROR: - case E_RECOVERABLE_ERROR: - $logLevel = LogLevel::ERROR; - break; - - case E_WARNING: - case E_USER_WARNING: - $logLevel = LogLevel::WARNING; - break; - - default: - $logLevel = LogLevel::NOTICE; - break; + throw $e; } - - self::$loggers['scream']->log($logLevel, $message, array( - 'type' => $level, - 'file' => $file, - 'line' => $line, - 'scream' => error_reporting(), - )); } } - return false; + return $type && $log; } /** - * Configure the error handler for delayed handling. + * Handles an exception by logging then forwarding it to an other handler. + * + * @param \Exception $exception An exception to handle + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public function handleException(\Exception $exception, array $error = null) + { + $level = error_reporting(); + if ($this->loggedErrors & E_ERROR & ($level | $this->screamedErrors)) { + $e = array( + 'type' => E_ERROR, + 'file' => $exception->getFile(), + 'line' => $exception->getLine(), + 'level' => $level, + 'stack' => $exception->getTrace(), + ); + if ($exception instanceof FatalErrorException) { + $message = 'Fatal '.$exception->getMessage(); + } elseif ($exception instanceof \ErrorException) { + $message = 'Uncaught '.$exception->getMessage(); + if ($exception instanceof ContextErrorException) { + $e['context'] = $exception->getContext(); + } + } else { + $message = 'Uncaught Exception: '.$exception->getMessage(); + } + if ($this->loggedErrors & $e['type']) { + $this->loggers[$e['type']][0]->log($this->loggers[$e['type']][1], $message, $e); + } + } + if ($exception instanceof FatalErrorException && !$exception instanceof OutOfMemoryException && $error) { + foreach ($this->getFatalErrorHandlers() as $handler) { + if ($e = $handler->handleError($error, $exception)) { + $exception = $e; + break; + } + } + } + if (empty($this->exceptionHandler)) { + throw $exception; // Give back $exception to the native handler + } + try { + call_user_func($this->exceptionHandler, $exception); + } catch (\Exception $handlerException) { + $this->exceptionHandler = null; + $this->handleException($handlerException); + } + } + + /** + * Shutdown registered function for handling PHP fatal errors. + * + * @param array $error An array as returned by error_get_last() + * + * @internal + */ + public static function handleFatalError(array $error = null) + { + self::$reservedMemory = ''; + gc_collect_cycles(); + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if ($handler instanceof self) { + if (null === $error) { + $error = error_get_last(); + } + + try { + while (self::$stackedErrorLevels) { + static::unstackErrors(); + } + } catch (\Exception $exception) { + // Handled below + } + + if ($error && ($error['type'] & (E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR))) { + // Let's not throw anymore but keep logging + $handler->throwAt(0, true); + + if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { + $exception = new OutOfMemoryException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, false); + } else { + $exception = new FatalErrorException($handler->levels[$error['type']].': '.$error['message'], 0, $error['type'], $error['file'], $error['line'], 2, true); + } + } elseif (!isset($exception)) { + return; + } + + try { + $handler->handleException($exception, $error); + } catch (FatalErrorException $e) { + // Ignore this re-throw + } + } + } + + /** + * Configures the error handler for delayed handling. * Ensures also that non-catchable fatal errors are never silenced. * * As shown by http://bugs.php.net/42098 and http://bugs.php.net/60724 @@ -219,7 +523,7 @@ class ErrorHandler } /** - * Unstacks stacked errors and forwards to the regular handler + * Unstacks stacked errors and forwards to the logger */ public static function unstackErrors() { @@ -237,64 +541,12 @@ class ErrorHandler $errors = self::$stackedErrors; self::$stackedErrors = array(); - $errorHandler = set_error_handler('var_dump'); - restore_error_handler(); - - if ($errorHandler) { - foreach ($errors as $e) { - call_user_func_array($errorHandler, $e); - } + foreach ($errors as $e) { + $e[0][0]->log($e[0][1], $e[1], $e[2]); } } } - public function handleFatal() - { - $this->reservedMemory = ''; - gc_collect_cycles(); - $error = error_get_last(); - - // get current exception handler - $exceptionHandler = set_exception_handler('var_dump'); - restore_exception_handler(); - - try { - while (self::$stackedErrorLevels) { - static::unstackErrors(); - } - } catch (\Exception $exception) { - if ($exceptionHandler) { - call_user_func($exceptionHandler, $exception); - - return; - } - - if ($this->displayErrors) { - ini_set('display_errors', 1); - } - - throw $exception; - } - - if (!$error || !$this->level || !($error['type'] & (E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR | E_PARSE))) { - return; - } - - if (isset(self::$loggers['emergency'])) { - $fatal = array( - 'type' => $error['type'], - 'file' => $error['file'], - 'line' => $error['line'], - ); - - self::$loggers['emergency']->emergency($error['message'], $fatal); - } - - if ($this->displayErrors && $exceptionHandler) { - $this->handleFatalError($exceptionHandler, $error); - } - } - /** * Gets the fatal error handlers. * @@ -311,32 +563,81 @@ class ErrorHandler ); } - private function handleFatalError($exceptionHandler, array $error) + /** + * Sets the level at which the conversion to Exception is done. + * + * @param int|null $level The level (null to use the error_reporting() value and 0 to disable) + * + * @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead. + */ + public function setLevel($level) { - // Let PHP handle any further error - set_error_handler('var_dump', 0); - ini_set('display_errors', 1); + $level = null === $level ? error_reporting() : $level; + $this->throwAt($level, true); + } - $exception = sprintf('%s: %s', $this->levels[$error['type']], $error['message']); - if (0 === strpos($error['message'], 'Allowed memory') || 0 === strpos($error['message'], 'Out of memory')) { - $exception = new OutOfMemoryException($exception, 0, $error['type'], $error['file'], $error['line'], 3, false); + /** + * Sets the display_errors flag value. + * + * @param int $displayErrors The display_errors flag value + * + * @deprecated since 2.6, to be removed in 3.0. Use throwAt() instead. + */ + public function setDisplayErrors($displayErrors) + { + if ($displayErrors) { + $this->throwAt($this->displayErrors, true); } else { - $exception = new FatalErrorException($exception, 0, $error['type'], $error['file'], $error['line'], 3, true); - - foreach ($this->getFatalErrorHandlers() as $handler) { - if ($e = $handler->handleError($error, $exception)) { - $exception = $e; - break; - } - } + $displayErrors = $this->displayErrors; + $this->throwAt(0, true); + $this->displayErrors = $displayErrors; } + } - try { - call_user_func($exceptionHandler, $exception); - } catch (\Exception $e) { - // The handler failed. Let PHP handle that now. - throw $exception; + /** + * Sets a logger for the given channel. + * + * @param LoggerInterface $logger A logger interface + * @param string $channel The channel associated with the logger (deprecation, emergency or scream) + * + * @deprecated since 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead. + */ + public static function setLogger(LoggerInterface $logger, $channel = 'deprecation') + { + $handler = set_error_handler('var_dump', 0); + $handler = is_array($handler) ? $handler[0] : null; + restore_error_handler(); + if (!$handler instanceof self) { + return; } + if ('deprecation' === $channel) { + $handler->setDefaultLogger($logger, E_DEPRECATED | E_USER_DEPRECATED, true); + $handler->screamAt(E_DEPRECATED | E_USER_DEPRECATED); + } elseif ('scream' === $channel) { + $handler->setDefaultLogger($logger, E_ALL | E_STRICT, false); + $handler->screamAt(E_ALL | E_STRICT); + } elseif ('emergency' === $channel) { + $handler->setDefaultLogger($logger, E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR, true); + $handler->screamAt(E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR); + } + } + + /** + * @deprecated since 2.6, to be removed in 3.0. Use handleError() instead. + */ + public function handle($level, $message, $file = 'unknown', $line = 0, $context = array()) + { + return $this->handleError($level, $message, $file, $line, (array) $context); + } + + /** + * Handles PHP fatal errors. + * + * @deprecated since 2.6, to be removed in 3.0. Use handleFatalError() instead. + */ + public function handleFatal() + { + static::handleFatalError(); } } diff --git a/src/Symfony/Component/Debug/ExceptionHandler.php b/src/Symfony/Component/Debug/ExceptionHandler.php index 02e4d7a150..f6ec8ddfb3 100644 --- a/src/Symfony/Component/Debug/ExceptionHandler.php +++ b/src/Symfony/Component/Debug/ExceptionHandler.php @@ -34,15 +34,13 @@ if (!defined('ENT_SUBSTITUTE')) { class ExceptionHandler { private $debug; - private $charset; private $handler; private $caughtBuffer; private $caughtLength; - public function __construct($debug = true, $charset = 'UTF-8') + public function __construct($debug = true) { $this->debug = $debug; - $this->charset = $charset; } /** @@ -56,7 +54,11 @@ class ExceptionHandler { $handler = new static($debug); - set_exception_handler(array($handler, 'handle')); + $prev = set_exception_handler(array($handler, 'handle')); + if (is_array($prev) && $prev[0] instanceof ErrorHandler) { + restore_exception_handler(); + $prev[0]->setExceptionHandler(array($handler, 'handle')); + } return $handler; } @@ -206,7 +208,7 @@ class ExceptionHandler foreach ($exception->toArray() as $position => $e) { $ind = $count - $position + 1; $class = $this->formatClass($e['class']); - $message = nl2br(htmlspecialchars($e['message'], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset)); + $message = nl2br(self::utf8Htmlize($e['message'])); $content .= sprintf(<<

%d/%d %s%s:
%s

@@ -344,7 +346,7 @@ EOF; private function formatPath($path, $line) { - $path = htmlspecialchars($path, ENT_QUOTES | ENT_SUBSTITUTE, $this->charset); + $path = self::utf8Htmlize($path); $file = preg_match('#[^/\\\\]*$#', $path, $file) ? $file[0] : $path; if ($linkFormat = ini_get('xdebug.file_link_format')) { @@ -372,7 +374,7 @@ EOF; } elseif ('array' === $item[0]) { $formattedValue = sprintf("array(%s)", is_array($item[1]) ? $this->formatArgs($item[1]) : $item[1]); } elseif ('string' === $item[0]) { - $formattedValue = sprintf("'%s'", htmlspecialchars($item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset)); + $formattedValue = sprintf("'%s'", self::utf8Htmlize($item[1])); } elseif ('null' === $item[0]) { $formattedValue = 'null'; } elseif ('boolean' === $item[0]) { @@ -380,7 +382,7 @@ EOF; } elseif ('resource' === $item[0]) { $formattedValue = 'resource'; } else { - $formattedValue = str_replace("\n", '', var_export(htmlspecialchars((string) $item[1], ENT_QUOTES | ENT_SUBSTITUTE, $this->charset), true)); + $formattedValue = str_replace("\n", '', var_export(self::utf8Htmlize((string) $item[1]), true)); } $result[] = is_int($key) ? $formattedValue : sprintf("'%s' => %s", $key, $formattedValue); @@ -389,6 +391,25 @@ EOF; return implode(', ', $result); } + /** + * Returns an UTF-8 and HTML encoded string + */ + protected static function utf8Htmlize($str) + { + if (!preg_match('//u', $str) && function_exists('iconv')) { + set_error_handler('var_dump', 0); + $charset = ini_get('default_charset'); + if ('UTF-8' === $charset || $str !== @iconv($charset, $charset, $str)) { + $charset = 'CP1252'; + } + restore_error_handler(); + + $str = iconv($charset, 'UTF-8', $str); + } + + return htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); + } + /** * @internal */ diff --git a/src/Symfony/Component/Debug/README.md b/src/Symfony/Component/Debug/README.md index dd5bdca6bf..e0ff2c1b2a 100644 --- a/src/Symfony/Component/Debug/README.md +++ b/src/Symfony/Component/Debug/README.md @@ -15,14 +15,13 @@ You can also use the tools individually: use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; - error_reporting(-1); - - ErrorHandler::register($errorReportingLevel); if ('cli' !== php_sapi_name()) { + ini_set('display_errors', 0); ExceptionHandler::register(); } elseif (!ini_get('log_errors') || ini_get('error_log')) { ini_set('display_errors', 1); } + ErrorHandler::register($errorReportingLevel); Note that the `Debug::enable()` call also registers the debug class loader from the Symfony ClassLoader component when available. diff --git a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php index 396cc98ee9..dd962d3c79 100644 --- a/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php +++ b/src/Symfony/Component/Debug/Tests/DebugClassLoaderTest.php @@ -106,11 +106,11 @@ class DebugClassLoaderTest extends \PHPUnit_Framework_TestCase $this->assertEquals(E_STRICT, $exception->getSeverity()); $this->assertStringStartsWith(__FILE__, $exception->getFile()); $this->assertRegexp('/^Runtime Notice: Declaration/', $exception->getMessage()); - } catch (\Exception $e) { + } catch (\Exception $exception) { restore_error_handler(); restore_exception_handler(); - throw $e; + throw $exception; } } diff --git a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php index c8fe549ea0..aaaefd54dd 100644 --- a/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ErrorHandlerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Debug\Tests; +use Psr\Log\LogLevel; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\Exception\ContextErrorException; @@ -18,6 +19,7 @@ use Symfony\Component\Debug\Exception\ContextErrorException; * ErrorHandlerTest * * @author Robert Schönthal + * @author Nicolas Grekas */ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase { @@ -54,6 +56,8 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase } catch (ContextErrorException $exception) { // if an exception is thrown, the test passed restore_error_handler(); + restore_exception_handler(); + $this->assertEquals(E_NOTICE, $exception->getSeverity()); $this->assertEquals(__FILE__, $exception->getFile()); $this->assertRegexp('/^Notice: Undefined variable: (foo|bar)/', $exception->getMessage()); @@ -62,7 +66,7 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase $trace = $exception->getTrace(); $this->assertEquals(__FILE__, $trace[0]['file']); $this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']); - $this->assertEquals('handle', $trace[0]['function']); + $this->assertEquals('handleError', $trace[0]['function']); $this->assertEquals('->', $trace[0]['type']); $this->assertEquals(__FILE__, $trace[1]['file']); @@ -76,11 +80,10 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase $this->assertEquals('->', $trace[2]['type']); } catch (\Exception $e) { restore_error_handler(); + restore_exception_handler(); throw $e; } - - restore_error_handler(); } // dummy function to test trace in error handler. @@ -94,78 +97,250 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase public function testConstruct() { try { - $handler = ErrorHandler::register(3); - - $level = new \ReflectionProperty($handler, 'level'); - $level->setAccessible(true); - - $this->assertEquals(3, $level->getValue($handler)); + $this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, ErrorHandler::register(3)->throwAt(0)); restore_error_handler(); + restore_exception_handler(); } catch (\Exception $e) { restore_error_handler(); + restore_exception_handler(); throw $e; } } - public function testHandle() + public function testDefaultLogger() + { + try { + $handler = ErrorHandler::register(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL)); + + $loggers = array( + E_DEPRECATED => array(null, LogLevel::INFO), + E_USER_DEPRECATED => array(null, LogLevel::INFO), + E_NOTICE => array($logger, LogLevel::NOTICE), + E_USER_NOTICE => array($logger, LogLevel::CRITICAL), + E_STRICT => array(null, LogLevel::NOTICE), + E_WARNING => array(null, LogLevel::WARNING), + E_USER_WARNING => array(null, LogLevel::WARNING), + E_COMPILE_WARNING => array(null, LogLevel::WARNING), + E_CORE_WARNING => array(null, LogLevel::WARNING), + E_USER_ERROR => array(null, LogLevel::ERROR), + E_RECOVERABLE_ERROR => array(null, LogLevel::ERROR), + E_COMPILE_ERROR => array(null, LogLevel::EMERGENCY), + E_PARSE => array(null, LogLevel::EMERGENCY), + E_ERROR => array(null, LogLevel::EMERGENCY), + E_CORE_ERROR => array(null, LogLevel::EMERGENCY), + ); + $this->assertSame($loggers, $handler->setLoggers(array())); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleError() { try { $handler = ErrorHandler::register(0); - $this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array())); + $this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array())); restore_error_handler(); + restore_exception_handler(); $handler = ErrorHandler::register(3); - $this->assertFalse($handler->handle(4, 'foo', 'foo.php', 12, array())); + $this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array())); restore_error_handler(); + restore_exception_handler(); $handler = ErrorHandler::register(3); try { - $handler->handle(4, 'foo', 'foo.php', 12, array()); + $handler->handleError(4, 'foo', 'foo.php', 12, array()); } catch (\ErrorException $e) { - $this->assertSame('Parse Error: foo in foo.php line 12', $e->getMessage()); + $this->assertSame('Parse Error: foo', $e->getMessage()); $this->assertSame(4, $e->getSeverity()); $this->assertSame('foo.php', $e->getFile()); $this->assertSame(12, $e->getLine()); } restore_error_handler(); + restore_exception_handler(); $handler = ErrorHandler::register(E_USER_DEPRECATED); - $this->assertFalse($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + $this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); restore_error_handler(); + restore_exception_handler(); $handler = ErrorHandler::register(E_DEPRECATED); - $this->assertFalse($handler->handle(E_DEPRECATED, 'foo', 'foo.php', 12, array())); + $this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array())); restore_error_handler(); + restore_exception_handler(); $logger = $this->getMock('Psr\Log\LoggerInterface'); $that = $this; - $warnArgCheck = function ($message, $context) use ($that) { + $warnArgCheck = function ($logLevel, $message, $context) use ($that) { + $that->assertEquals('info', $logLevel); $that->assertEquals('foo', $message); $that->assertArrayHasKey('type', $context); - $that->assertEquals($context['type'], ErrorHandler::TYPE_DEPRECATION); + $that->assertEquals($context['type'], E_USER_DEPRECATED); $that->assertArrayHasKey('stack', $context); $that->assertInternalType('array', $context['stack']); }; $logger ->expects($this->once()) - ->method('warning') + ->method('log') ->will($this->returnCallback($warnArgCheck)) ; $handler = ErrorHandler::register(E_USER_DEPRECATED); - $handler->setLogger($logger); - $this->assertTrue($handler->handle(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); + $handler->setDefaultLogger($logger, E_USER_DEPRECATED); + $this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array())); restore_error_handler(); + restore_exception_handler(); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $that = $this; + $logArgCheck = function ($level, $message, $context) use ($that) { + $that->assertEquals('Undefined variable: undefVar', $message); + $that->assertArrayHasKey('type', $context); + $that->assertEquals($context['type'], E_NOTICE); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler = ErrorHandler::register(E_NOTICE); + $handler->setDefaultLogger($logger, E_NOTICE); + $handler->screamAt(E_NOTICE); + unset($undefVar); + @$undefVar++; + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleException() + { + try { + $handler = ErrorHandler::register(); + + $exception = new \Exception('foo'); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $that = $this; + $logArgCheck = function ($level, $message, $context) use ($that) { + $that->assertEquals('Uncaught Exception: foo', $message); + $that->assertArrayHasKey('type', $context); + $that->assertEquals($context['type'], E_ERROR); + }; + + $logger + ->expects($this->exactly(2)) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + try { + $handler->handleException($exception); + $this->fail('Exception expected'); + } catch (\Exception $e) { + $this->assertSame($exception, $e); + } + + $that = $this; + $handler->setExceptionHandler(function ($e) use ($exception, $that) { + $that->assertSame($exception, $e); + }); + + $handler->handleException($exception); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testHandleFatalError() + { + try { + $handler = ErrorHandler::register(); + + $error = array( + 'type' => E_PARSE, + 'message' => 'foo', + 'file' => 'bar', + 'line' => 123, + ); + + $logger = $this->getMock('Psr\Log\LoggerInterface'); + + $that = $this; + $logArgCheck = function ($level, $message, $context) use ($that) { + $that->assertEquals('Fatal Parse Error: foo', $message); + $that->assertArrayHasKey('type', $context); + $that->assertEquals($context['type'], E_ERROR); + }; + + $logger + ->expects($this->once()) + ->method('log') + ->will($this->returnCallback($logArgCheck)) + ; + + $handler->setDefaultLogger($logger, E_ERROR); + + $handler->handleFatalError($error); + + restore_error_handler(); + restore_exception_handler(); + } catch (\Exception $e) { + restore_error_handler(); + restore_exception_handler(); + + throw $e; + } + } + + public function testDeprecated() + { + try { + $handler = ErrorHandler::register(0); + $this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array())); + + restore_error_handler(); + restore_exception_handler(); $logger = $this->getMock('Psr\Log\LoggerInterface'); @@ -188,8 +363,10 @@ class ErrorHandlerTest extends \PHPUnit_Framework_TestCase @$undefVar++; restore_error_handler(); + restore_exception_handler(); } catch (\Exception $e) { restore_error_handler(); + restore_exception_handler(); throw $e; } diff --git a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php index b202b8f1de..1e0859ebd8 100644 --- a/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/ExceptionHandlerTest.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Debug\Tests; use Symfony\Component\Debug\ExceptionHandler; +use Symfony\Component\Debug\Exception\OutOfMemoryException; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException; @@ -59,4 +61,56 @@ class ExceptionHandlerTest extends \PHPUnit_Framework_TestCase $handler = new ExceptionHandler(true); $response = $handler->createResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar'))); } + + public function testHandle() + { + $exception = new \Exception('foo'); + + if (class_exists('Symfony\Component\HttpFoundation\Response')) { + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse')); + $handler + ->expects($this->exactly(2)) + ->method('createResponse') + ->will($this->returnValue(new Response())); + } else { + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->exactly(2)) + ->method('sendPhpResponse'); + } + + $handler->handle($exception); + + $that = $this; + $handler->setHandler(function ($e) use ($exception, $that) { + $that->assertSame($exception, $e); + }); + + $handler->handle($exception); + } + + public function testHandleOutOfMemoryException() + { + $exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__); + + if (class_exists('Symfony\Component\HttpFoundation\Response')) { + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('createResponse')); + $handler + ->expects($this->once()) + ->method('createResponse') + ->will($this->returnValue(new Response())); + } else { + $handler = $this->getMock('Symfony\Component\Debug\ExceptionHandler', array('sendPhpResponse')); + $handler + ->expects($this->once()) + ->method('sendPhpResponse'); + } + + $that = $this; + $handler->setHandler(function ($e) use ($that) { + $that->fail('OutOfMemoryException should bypass the handler'); + }); + + $handler->handle($exception); + } } diff --git a/src/Symfony/Component/Debug/composer.json b/src/Symfony/Component/Debug/composer.json index 447ecc8e9b..b919aa4fd6 100644 --- a/src/Symfony/Component/Debug/composer.json +++ b/src/Symfony/Component/Debug/composer.json @@ -16,7 +16,8 @@ } ], "require": { - "php": ">=5.3.3" + "php": ">=5.3.3", + "psr/log": "~1.0" }, "require-dev": { "symfony/http-kernel": "~2.1", diff --git a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php index 48cde6695b..0d1decda3a 100644 --- a/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php +++ b/src/Symfony/Component/HttpKernel/DataCollector/LoggerDataCollector.php @@ -11,7 +11,6 @@ namespace Symfony\Component\HttpKernel\DataCollector; -use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Log\DebugLoggerInterface; @@ -99,7 +98,11 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte private function sanitizeLogs($logs) { foreach ($logs as $i => $log) { - $logs[$i]['context'] = $this->sanitizeContext($log['context']); + $context = $this->sanitizeContext($log['context']); + if (isset($context['type'], $context['level']) && !($context['type'] & $context['level'])) { + $context['scream'] = true; + } + $logs[$i]['context'] = $context; } return $logs; @@ -145,10 +148,10 @@ class LoggerDataCollector extends DataCollector implements LateDataCollectorInte ); } - if (isset($log['context']['type'])) { - if (ErrorHandler::TYPE_DEPRECATION === $log['context']['type']) { + if (isset($log['context']['type'], $log['context']['level'])) { + if (E_DEPRECATED === $log['context']['type'] || E_USER_DEPRECATED === $log['context']['type']) { ++$count['deprecation_count']; - } elseif (isset($log['context']['scream'])) { + } elseif (!($log['context']['type'] & $log['context']['level'])) { ++$count['scream_count']; } } diff --git a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php index cdb06af9ff..86d8812530 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DataCollector/LoggerDataCollectorTest.php @@ -12,7 +12,6 @@ namespace Symfony\Component\HttpKernel\Tests\DataCollector; use Symfony\Component\HttpKernel\DataCollector\LoggerDataCollector; -use Symfony\Component\HttpKernel\Debug\ErrorHandler; class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase { @@ -66,14 +65,20 @@ class LoggerDataCollectorTest extends \PHPUnit_Framework_TestCase array( 1, array( - array('message' => 'foo', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION), 'priority' => 100, 'priorityName' => 'DEBUG'), - array('message' => 'foo2', 'context' => array('type' => ErrorHandler::TYPE_DEPRECATION), 'priority' => 100, 'priorityName' => 'DEBUG'), - array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'scream' => 0), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo', 'context' => array('type' => E_DEPRECATED, 'level' => E_ALL), 'priority' => 100, 'priorityName' => 'DEBUG'), + array('message' => 'foo2', 'context' => array('type' => E_USER_DEPRECATED, 'level' => E_ALL), 'priority' => 100, 'priorityName' => 'DEBUG'), ), null, 2, + 0, + array(100 => array('count' => 2, 'name' => 'DEBUG')), + ), + array( + 1, + array(array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0), 'priority' => 100, 'priorityName' => 'DEBUG')), + array(array('message' => 'foo3', 'context' => array('type' => E_USER_WARNING, 'level' => 0, 'scream' => true), 'priority' => 100, 'priorityName' => 'DEBUG')), + 0, 1, - array(100 => array('count' => 3, 'name' => 'DEBUG')), ), ); }