Compare commits

..

5 Commits

Author SHA1 Message Date
Grégoire Paris
74251c8d50 Bump phpunit XSD version to 5.2
Some attributes being used in the phpunit configuration files, namely
failOnRisky and failOnWarning were introduced in phpunit 5.2.0. The
Composer configuration shows that tests should run with old versions of
phpunit, but phpunit only validates the configuration against the XSD
since phpunit 7.2.0.
These changes can be tested as follows:

wget http://schema.phpunit.de/5.2/phpunit.xsd
xargs xmllint --schema phpunit.xsd  1>/dev/null
find src -name phpunit.xml.dist| xargs xmllint --schema phpunit.xsd  1>/dev/null

See 7e06a82806
See 46e3745a03/composer.json (L98)
2018-11-11 12:18:13 +01:00
François-Xavier de Guillebon
6a198c52b6 Fix ini_get() for boolean values 2018-10-30 17:24:01 +01:00
Nicolas Grekas
087890e02f [Debug] fix compat with PHP 7.3 2018-10-02 18:22:14 +02:00
Gabriel Caruso
65d41d1696 [CS] Enforces null type hint on last position in phpDocs 2018-10-02 00:12:00 -03:00
Nicolas Grekas
4fd77efcd4 Fix CS 2018-09-21 14:46:38 +02:00
29 changed files with 874 additions and 954 deletions

View File

@@ -1,28 +1,6 @@
CHANGELOG
=========
3.4.0
-----
* deprecated `ErrorHandler::stackErrors()` and `ErrorHandler::unstackErrors()`
3.3.0
-----
* deprecated the `ContextErrorException` class: use \ErrorException directly now
3.2.0
-----
* `FlattenException::getTrace()` now returns additional type descriptions
`integer` and `float`.
3.0.0
-----
* removed classes, methods and interfaces deprecated in 2.x
2.8.0
-----

View File

@@ -28,7 +28,7 @@ class Debug
* @param int $errorReportingLevel The level of error reporting you want
* @param bool $displayErrors Whether to display errors (for development) or just log them (for production)
*/
public static function enable($errorReportingLevel = E_ALL, $displayErrors = true)
public static function enable($errorReportingLevel = null, $displayErrors = true)
{
if (static::$enabled) {
return;
@@ -39,13 +39,13 @@ class Debug
if (null !== $errorReportingLevel) {
error_reporting($errorReportingLevel);
} else {
error_reporting(E_ALL);
error_reporting(-1);
}
if (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
ini_set('display_errors', 0);
ExceptionHandler::register();
} elseif ($displayErrors && (!ini_get('log_errors') || ini_get('error_log'))) {
} elseif ($displayErrors && (!filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) {
// CLI - display errors only if they're not already logged to STDERR
ini_set('display_errors', 1);
}

View File

@@ -27,20 +27,27 @@ class DebugClassLoader
private $classLoader;
private $isFinder;
private $loaded = array();
private $wasFinder;
private static $caseCheck;
private static $checkedClasses = array();
private static $final = array();
private static $finalMethods = array();
private static $deprecated = array();
private static $internal = array();
private static $internalMethods = array();
private static $php7Reserved = array('int' => 1, 'float' => 1, 'bool' => 1, 'string' => 1, 'true' => 1, 'false' => 1, 'null' => 1);
private static $php7Reserved = array('int', 'float', 'bool', 'string', 'true', 'false', 'null');
private static $darwinCache = array('/' => array('/', array()));
public function __construct(callable $classLoader)
/**
* @param callable|object $classLoader Passing an object is @deprecated since version 2.5 and support for it will be removed in 3.0
*/
public function __construct($classLoader)
{
$this->classLoader = $classLoader;
$this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
$this->wasFinder = \is_object($classLoader) && method_exists($classLoader, 'findFile');
if ($this->wasFinder) {
@trigger_error('The '.__METHOD__.' method will no longer support receiving an object into its $classLoader argument in 3.0.', E_USER_DEPRECATED);
$this->classLoader = array($classLoader, 'loadClass');
$this->isFinder = true;
} else {
$this->classLoader = $classLoader;
$this->isFinder = \is_array($classLoader) && method_exists($classLoader[0], 'findFile');
}
if (!isset(self::$caseCheck)) {
$file = file_exists(__FILE__) ? __FILE__ : rtrim(realpath('.'), \DIRECTORY_SEPARATOR);
@@ -69,11 +76,11 @@ class DebugClassLoader
/**
* Gets the wrapped class loader.
*
* @return callable The wrapped class loader
* @return callable|object A class loader. Since version 2.5, returning an object is @deprecated and support for it will be removed in 3.0
*/
public function getClassLoader()
{
return $this->classLoader;
return $this->wasFinder ? $this->classLoader[0] : $this->classLoader;
}
/**
@@ -124,6 +131,24 @@ class DebugClassLoader
}
}
/**
* Finds a file by class name.
*
* @param string $class A class name to resolve to file
*
* @return string|null
*
* @deprecated since version 2.5, to be removed in 3.0.
*/
public function findFile($class)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
if ($this->wasFinder) {
return $this->classLoader[0]->findFile($class);
}
}
/**
* Loads the given class or interface.
*
@@ -135,152 +160,85 @@ class DebugClassLoader
*/
public function loadClass($class)
{
$e = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
ErrorHandler::stackErrors();
try {
if ($this->isFinder && !isset($this->loaded[$class])) {
$this->loaded[$class] = true;
if ($file = $this->classLoader[0]->findFile($class) ?: false) {
$wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file);
if ($file = $this->classLoader[0]->findFile($class)) {
require $file;
if ($wasCached) {
return;
}
}
} else {
\call_user_func($this->classLoader, $class);
$file = false;
}
} finally {
error_reporting($e);
} catch (\Exception $e) {
ErrorHandler::unstackErrors();
throw $e;
} catch (\Throwable $e) {
ErrorHandler::unstackErrors();
throw $e;
}
$this->checkClass($class, $file);
}
ErrorHandler::unstackErrors();
private function checkClass($class, $file = null)
{
$exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false);
$exists = class_exists($class, false) || interface_exists($class, false) || (\function_exists('trait_exists') && trait_exists($class, false));
if (null !== $file && $class && '\\' === $class[0]) {
if ($class && '\\' === $class[0]) {
$class = substr($class, 1);
}
if ($exists) {
if (isset(self::$checkedClasses[$class])) {
return;
}
self::$checkedClasses[$class] = true;
$refl = new \ReflectionClass($class);
if (null === $file && $refl->isInternal()) {
return;
}
$name = $refl->getName();
if ($name !== $class && 0 === \strcasecmp($name, $class)) {
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
if ($name !== $class && 0 === strcasecmp($name, $class)) {
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: %s vs %s', $class, $name));
}
// Don't trigger deprecations for classes in the same vendor
if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) {
$len = 0;
$ns = '';
if (\in_array(strtolower($refl->getShortName()), self::$php7Reserved)) {
@trigger_error(sprintf('%s uses a reserved class name (%s) that will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
} elseif (preg_match('#\n \* @deprecated (.*?)\r?\n \*(?: @|/$)#s', $refl->getDocComment(), $notice)) {
self::$deprecated[$name] = preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]);
} else {
$ns = \substr($name, 0, $len);
}
if (2 > $len = 1 + (strpos($name, '\\') ?: strpos($name, '_'))) {
$len = 0;
$ns = '';
} else {
$ns = substr($name, 0, $len);
}
$parent = get_parent_class($class);
// Detect annotations on the class
if (false !== $doc = $refl->getDocComment()) {
foreach (array('final', 'deprecated', 'internal') as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n \* @'.$annotation.'(?:( .+?)\.?)?\r?\n \*(?: @|/$)#s', $doc, $notice)) {
self::${$annotation}[$name] = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
if (!$parent || strncmp($ns, $parent, $len)) {
if ($parent && isset(self::$deprecated[$parent]) && strncmp($ns, $parent, $len)) {
@trigger_error(sprintf('The %s class extends %s that is deprecated %s', $name, $parent, self::$deprecated[$parent]), E_USER_DEPRECATED);
}
}
}
$parentAndTraits = \class_uses($name, false);
if ($parent = \get_parent_class($class)) {
$parentAndTraits[] = $parent;
if (!isset(self::$checkedClasses[$parent])) {
$this->checkClass($parent);
}
if (isset(self::$final[$parent])) {
@trigger_error(sprintf('The "%s" class is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $parent, self::$final[$parent], $name), E_USER_DEPRECATED);
}
}
// Detect if the parent is annotated
foreach ($parentAndTraits + $this->getOwnInterfaces($name, $parent) as $use) {
if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use);
}
if (isset(self::$deprecated[$use]) && \strncmp($ns, $use, $len)) {
$type = class_exists($name, false) ? 'class' : (interface_exists($name, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($name, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
@trigger_error(sprintf('The "%s" %s %s "%s" that is deprecated%s.', $name, $type, $verb, $use, self::$deprecated[$use]), E_USER_DEPRECATED);
}
if (isset(self::$internal[$use]) && \strncmp($ns, $use, $len)) {
@trigger_error(sprintf('The "%s" %s is considered internal%s. It may change without further notice. You should not use it from "%s".', $use, class_exists($use, false) ? 'class' : (interface_exists($use, false) ? 'interface' : 'trait'), self::$internal[$use], $name), E_USER_DEPRECATED);
}
}
// Inherit @final and @internal annotations for methods
self::$finalMethods[$name] = array();
self::$internalMethods[$name] = array();
foreach ($parentAndTraits as $use) {
foreach (array('finalMethods', 'internalMethods') as $property) {
if (isset(self::${$property}[$use])) {
self::${$property}[$name] = self::${$property}[$name] ? self::${$property}[$use] + self::${$property}[$name] : self::${$property}[$use];
$parentInterfaces = array();
$deprecatedInterfaces = array();
if ($parent) {
foreach (class_implements($parent) as $interface) {
$parentInterfaces[$interface] = 1;
}
}
}
}
$isClass = \class_exists($name, false);
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
if ($method->class !== $name) {
continue;
}
foreach ($refl->getInterfaceNames() as $interface) {
if (isset(self::$deprecated[$interface]) && strncmp($ns, $interface, $len)) {
$deprecatedInterfaces[] = $interface;
}
foreach (class_implements($interface) as $interface) {
$parentInterfaces[$interface] = 1;
}
}
// Method from a trait
if ($method->getFilename() !== $refl->getFileName()) {
continue;
}
if ($isClass && $parent && isset(self::$finalMethods[$parent][$method->name])) {
list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
@trigger_error(sprintf('The "%s::%s()" method is considered final%s. It may change without further notice as of its next major version. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
}
foreach ($parentAndTraits as $use) {
if (isset(self::$internalMethods[$use][$method->name])) {
list($declaringClass, $message) = self::$internalMethods[$use][$method->name];
if (\strncmp($ns, $declaringClass, $len)) {
@trigger_error(sprintf('The "%s::%s()" method is considered internal%s. It may change without further notice. You should not extend it from "%s".', $declaringClass, $method->name, $message, $name), E_USER_DEPRECATED);
foreach ($deprecatedInterfaces as $interface) {
if (!isset($parentInterfaces[$interface])) {
@trigger_error(sprintf('The %s %s %s that is deprecated %s', $name, $refl->isInterface() ? 'interface extends' : 'class implements', $interface, self::$deprecated[$interface]), E_USER_DEPRECATED);
}
}
}
// Detect method annotations
if (false === $doc = $method->getDocComment()) {
continue;
}
foreach (array('final', 'internal') as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$)#s', $doc, $notice)) {
$message = isset($notice[1]) ? preg_replace('#\s*\r?\n \* +#', ' ', $notice[1]) : '';
self::${$annotation.'Methods'}[$name][$method->name] = array($name, $message);
}
}
}
if (isset(self::$php7Reserved[\strtolower($refl->getShortName())])) {
@trigger_error(sprintf('The "%s" class uses the reserved name "%s", it will break on PHP 7 and higher', $name, $refl->getShortName()), E_USER_DEPRECATED);
}
}
@@ -374,36 +332,11 @@ class DebugClassLoader
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
) {
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: %s vs %s in %s', substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)));
}
}
return true;
}
}
/**
* `class_implements` includes interfaces from the parents so we have to manually exclude them.
*
* @param string $class
* @param string|false $parent
*
* @return string[]
*/
private function getOwnInterfaces($class, $parent)
{
$ownInterfaces = class_implements($class, false);
if ($parent) {
foreach (class_implements($parent, false) as $interface) {
unset($ownInterfaces[$interface]);
}
}
foreach ($ownInterfaces as $interface) {
foreach (class_implements($interface) as $interface) {
unset($ownInterfaces[$interface]);
}
}
return $ownInterfaces;
}
}

View File

@@ -17,7 +17,6 @@ use Symfony\Component\Debug\Exception\ContextErrorException;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
use Symfony\Component\Debug\FatalErrorHandler\FatalErrorHandlerInterface;
use Symfony\Component\Debug\FatalErrorHandler\UndefinedFunctionFatalErrorHandler;
@@ -30,7 +29,7 @@ use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
* - 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
* - 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.
@@ -44,10 +43,14 @@ use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
* can see them and weight them as more important to fix than others of the same level.
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class ErrorHandler
{
/**
* @deprecated since version 2.6, to be removed in 3.0.
*/
const TYPE_DEPRECATION = -100;
private $levels = array(
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
@@ -89,8 +92,8 @@ class ErrorHandler
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 $traceReflector;
private $loggedTraces = array();
private $isRecursive = 0;
private $isRoot = false;
private $exceptionHandler;
@@ -100,26 +103,38 @@ class ErrorHandler
private static $stackedErrors = array();
private static $stackedErrorLevels = array();
private static $toStringException = null;
private static $silencedErrorCache = array();
private static $silencedErrorCount = 0;
private static $exitCode = 0;
/**
* Same init value as thrownErrors.
*
* @deprecated since version 2.6, to be removed in 3.0.
*/
private $displayErrors = 0x1FFF;
/**
* Registers the error handler.
*
* @param self|null $handler The handler to register
* @param bool $replace Whether to replace or not any existing handler
* @param self|int|null $handler The handler to register, or @deprecated (since version 2.6, to be removed in 3.0) bit field of thrown levels
* @param bool $replace Whether to replace or not any existing handler
*
* @return self The registered error handler
*/
public static function register(self $handler = null, $replace = true)
public static function register($handler = null, $replace = true)
{
if (null === self::$reservedMemory) {
self::$reservedMemory = str_repeat('x', 10240);
register_shutdown_function(__CLASS__.'::handleFatalError');
}
if ($handlerIsNew = null === $handler) {
$levels = -1;
if ($handlerIsNew = !$handler instanceof self) {
// @deprecated polymorphism, to be removed in 3.0
if (null !== $handler) {
$levels = $replace ? $handler : 0;
$replace = true;
}
$handler = new static();
}
@@ -154,7 +169,7 @@ class ErrorHandler
$handler->setExceptionHandler($prev);
}
$handler->throwAt(E_ALL & $handler->thrownErrors, true);
$handler->throwAt($levels & $handler->thrownErrors, true);
return $handler;
}
@@ -165,8 +180,6 @@ class ErrorHandler
$this->bootstrappingLogger = $bootstrappingLogger;
$this->setDefaultLogger($bootstrappingLogger);
}
$this->traceReflector = new \ReflectionProperty('Exception', 'trace');
$this->traceReflector->setAccessible(true);
}
/**
@@ -176,7 +189,7 @@ class ErrorHandler
* @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 setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
public function setDefaultLogger(LoggerInterface $logger, $levels = null, $replace = false)
{
$loggers = array();
@@ -188,7 +201,7 @@ class ErrorHandler
}
} else {
if (null === $levels) {
$levels = E_ALL;
$levels = E_ALL | E_STRICT;
}
foreach ($this->loggers as $type => $log) {
if (($type & $levels) && (empty($log[0]) || $replace || $log[0] === $this->bootstrappingLogger)) {
@@ -242,7 +255,7 @@ class ErrorHandler
if ($flush) {
foreach ($this->bootstrappingLogger->cleanLogs() as $log) {
$type = $log[2]['exception'] instanceof \ErrorException ? $log[2]['exception']->getSeverity() : E_ERROR;
$type = $log[2]['type'];
if (!isset($flush[$type])) {
$this->bootstrappingLogger->log($log[0], $log[1], $log[2]);
} elseif ($this->loggers[$type][0]) {
@@ -260,9 +273,14 @@ class ErrorHandler
* @param callable $handler A handler that will be called on Exception
*
* @return callable|null The previous exception handler
*
* @throws \InvalidArgumentException
*/
public function setExceptionHandler(callable $handler = null)
public function setExceptionHandler($handler)
{
if (null !== $handler && !\is_callable($handler)) {
throw new \LogicException('The exception handler must be a valid PHP callable.');
}
$prev = $this->exceptionHandler;
$this->exceptionHandler = $handler;
@@ -286,6 +304,9 @@ class ErrorHandler
}
$this->reRegister($prev | $this->loggedErrors);
// $this->displayErrors is @deprecated since version 2.6
$this->displayErrors = $this->thrownErrors;
return $prev;
}
@@ -382,10 +403,8 @@ class ErrorHandler
*/
public function handleError($type, $message, $file, $line)
{
// Level is the current error reporting level to manage silent error.
$level = error_reporting();
$silenced = 0 === ($level & $type);
// Strong errors are not authorized to be silenced.
$level |= E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED;
$log = $this->loggedErrors & $type;
$throw = $this->thrownErrors & $type & $level;
@@ -419,53 +438,28 @@ class ErrorHandler
return true;
}
$logMessage = $this->levels[$type].': '.$message;
if (null !== self::$toStringException) {
$errorAsException = self::$toStringException;
self::$toStringException = null;
} elseif (!$throw && !($type & $level)) {
if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
$lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : array();
$errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace);
} elseif (isset(self::$silencedErrorCache[$id][$message])) {
$lightTrace = null;
$errorAsException = self::$silencedErrorCache[$id][$message];
++$errorAsException->count;
} else {
$lightTrace = array();
$errorAsException = null;
}
if (100 < ++self::$silencedErrorCount) {
self::$silencedErrorCache = $lightTrace = array();
self::$silencedErrorCount = 1;
}
if ($errorAsException) {
self::$silencedErrorCache[$id][$message] = $errorAsException;
}
if (null === $lightTrace) {
return;
}
} else {
if ($scope) {
$errorAsException = new ContextErrorException($logMessage, 0, $type, $file, $line, $context);
} else {
$errorAsException = new \ErrorException($logMessage, 0, $type, $file, $line);
}
// Clean the trace by removing function arguments and the first frames added by the error handler itself.
if ($throw || $this->tracedErrors & $type) {
$backtrace = $backtrace ?: $errorAsException->getTrace();
$lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
$this->traceReflector->setValue($errorAsException, $lightTrace);
} else {
$this->traceReflector->setValue($errorAsException, array());
}
}
if ($throw) {
if (null !== self::$toStringException) {
$throw = self::$toStringException;
self::$toStringException = null;
} elseif ($scope && 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 {
$throw = new \ErrorException($this->levels[$type].': '.$message, 0, $type, $file, $line);
}
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();
}
if (E_USER_ERROR & $type) {
$backtrace = $backtrace ?: $throw->getTrace();
for ($i = 1; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
&& '__toString' === $backtrace[$i]['function']
@@ -484,7 +478,7 @@ class ErrorHandler
if (($e instanceof \Exception || $e instanceof \Throwable) && $e->__toString() === $message) {
if (1 === $i) {
// On HHVM
$errorAsException = $e;
$throw = $e;
break;
}
self::$toStringException = $e;
@@ -495,7 +489,7 @@ class ErrorHandler
if (1 < $i) {
// On PHP (not on HHVM), display the original error message instead of the default one.
$this->handleException($errorAsException);
$this->handleException($throw);
// Stop the process by giving back the error to the native handler.
return false;
@@ -504,25 +498,56 @@ class ErrorHandler
}
}
throw $errorAsException;
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 ($scope) {
$e['scope_vars'] = $context;
if ($trace) {
$e['stack'] = $backtrace ?: debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT);
}
} elseif ($trace) {
if (null === $backtrace) {
$e['stack'] = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
} else {
foreach ($backtrace as &$frame) {
unset($frame['args'], $frame);
}
$e['stack'] = $backtrace;
}
}
}
if ($this->isRecursive) {
$log = 0;
} elseif (self::$stackedErrorLevels) {
self::$stackedErrors[] = array(
$this->loggers[$type][0],
($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG,
$logMessage,
$errorAsException ? array('exception' => $errorAsException) : array(),
);
self::$stackedErrors[] = array($this->loggers[$type][0], ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
} else {
try {
$this->isRecursive = true;
$level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
$this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? array('exception' => $errorAsException) : array());
} finally {
$this->loggers[$type][0]->log(($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG, $message, $e);
$this->isRecursive = false;
} catch (\Exception $e) {
$this->isRecursive = false;
throw $e;
} catch (\Throwable $e) {
$this->isRecursive = false;
throw $e;
}
}
@@ -549,26 +574,36 @@ class ErrorHandler
$handlerException = null;
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
$e = array(
'type' => $type,
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'level' => error_reporting(),
'stack' => $exception->getTrace(),
);
if ($exception instanceof FatalErrorException) {
if ($exception instanceof FatalThrowableError) {
$error = array(
'type' => $type,
'message' => $message = $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'file' => $e['file'],
'line' => $e['line'],
);
} else {
$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 & $type) {
try {
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception));
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, $e);
} catch (\Exception $handlerException) {
} catch (\Throwable $handlerException) {
}
@@ -696,32 +731,24 @@ class ErrorHandler
*
* The most important feature of this is to prevent
* autoloading until unstackErrors() is called.
*
* @deprecated since version 3.4, to be removed in 4.0.
*/
public static function stackErrors()
{
@trigger_error('Support for stacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
self::$stackedErrorLevels[] = error_reporting(error_reporting() | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR);
}
/**
* Unstacks stacked errors and forwards to the logger.
*
* @deprecated since version 3.4, to be removed in 4.0.
*/
public static function unstackErrors()
{
@trigger_error('Support for unstacking errors is deprecated since Symfony 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
$level = array_pop(self::$stackedErrorLevels);
if (null !== $level) {
$errorReportingLevel = error_reporting($level);
if ($errorReportingLevel !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
$e = error_reporting($level);
if ($e !== ($level | E_PARSE | E_ERROR | E_CORE_ERROR | E_COMPILE_ERROR)) {
// If the user changed the error level, do not overwrite it
error_reporting($errorReportingLevel);
error_reporting($e);
}
}
@@ -729,8 +756,8 @@ class ErrorHandler
$errors = self::$stackedErrors;
self::$stackedErrors = array();
foreach ($errors as $error) {
$error[0]->log($error[1], $error[2], $error[3]);
foreach ($errors as $e) {
$e[0]->log($e[1], $e[2], $e[3]);
}
}
}
@@ -751,22 +778,117 @@ class ErrorHandler
);
}
private function cleanTrace($backtrace, $type, $file, $line, $throw)
/**
* 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 version 2.6, to be removed in 3.0. Use throwAt() instead.
*/
public function setLevel($level)
{
$lightTrace = $backtrace;
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
for ($i = 0; isset($backtrace[$i]); ++$i) {
if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) {
$lightTrace = \array_slice($lightTrace, 1 + $i);
break;
}
}
if (!($throw || $this->scopedErrors & $type)) {
for ($i = 0; isset($lightTrace[$i]); ++$i) {
unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);
}
}
$level = null === $level ? error_reporting() : $level;
$this->throwAt($level, true);
}
return $lightTrace;
/**
* Sets the display_errors flag value.
*
* @param int $displayErrors The display_errors flag value
*
* @deprecated since version 2.6, to be removed in 3.0. Use throwAt() instead.
*/
public function setDisplayErrors($displayErrors)
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the throwAt() method instead.', E_USER_DEPRECATED);
if ($displayErrors) {
$this->throwAt($this->displayErrors, true);
} else {
$displayErrors = $this->displayErrors;
$this->throwAt(0, true);
$this->displayErrors = $displayErrors;
}
}
/**
* 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 version 2.6, to be removed in 3.0. Use setLoggers() or setDefaultLogger() instead.
*/
public static function setLogger(LoggerInterface $logger, $channel = 'deprecation')
{
@trigger_error('The '.__METHOD__.' static method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the setLoggers() or setDefaultLogger() methods instead.', E_USER_DEPRECATED);
$handler = set_error_handler('var_dump');
$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 version 2.6, to be removed in 3.0. Use handleError() instead.
*/
public function handle($level, $message, $file = 'unknown', $line = 0, $context = array())
{
$this->handleError(E_USER_DEPRECATED, 'The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the handleError() method instead.', __FILE__, __LINE__, array());
return $this->handleError($level, $message, $file, $line, (array) $context);
}
/**
* Handles PHP fatal errors.
*
* @deprecated since version 2.6, to be removed in 3.0. Use handleFatalError() instead.
*/
public function handleFatal()
{
@trigger_error('The '.__METHOD__.' method is deprecated since Symfony 2.6 and will be removed in 3.0. Use the handleFatalError() method instead.', E_USER_DEPRECATED);
static::handleFatalError();
}
}
/**
* Private class used to work around https://bugs.php.net/54275.
*
* @author Nicolas Grekas <p@tchwork.com>
*
* @internal
*/
class ErrorHandlerCanary
{
private static $displayErrors = null;
public function __construct()
{
if (null === self::$displayErrors) {
self::$displayErrors = ini_set('display_errors', 1);
}
}
public function __destruct()
{
if (null !== self::$displayErrors) {
ini_set('display_errors', self::$displayErrors);
self::$displayErrors = null;
}
}
}

View File

@@ -15,8 +15,6 @@ namespace Symfony\Component\Debug\Exception;
* Error Exception with Variable Context.
*
* @author Christian Sciberras <uuf6429@gmail.com>
*
* @deprecated since version 3.3. Instead, \ErrorException will be used directly in 4.0.
*/
class ContextErrorException extends \ErrorException
{
@@ -33,8 +31,6 @@ class ContextErrorException extends \ErrorException
*/
public function getContext()
{
@trigger_error(sprintf('The %s class is deprecated since Symfony 3.3 and will be removed in 4.0.', __CLASS__), E_USER_DEPRECATED);
return $this->context;
}
}

View File

@@ -0,0 +1,23 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug\Exception;
@trigger_error('The '.__NAMESPACE__.'\DummyException class is deprecated since Symfony 2.5 and will be removed in 3.0.', E_USER_DEPRECATED);
/**
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since version 2.5, to be removed in 3.0.
*/
class DummyException extends \ErrorException
{
}

View File

@@ -9,14 +9,31 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Exception;
/**
* Fatal Error Exception.
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Konstanton Myakshin <koc-dp@yandex.ru>
* @author Nicolas Grekas <p@tchwork.com>
*
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
*/
class FatalErrorException extends \ErrorException
{
}
namespace Symfony\Component\Debug\Exception;
use Symfony\Component\HttpKernel\Exception\FatalErrorException as LegacyFatalErrorException;
/**
* Fatal Error Exception.
*
* @author Konstanton Myakshin <koc-dp@yandex.ru>
*/
class FatalErrorException extends \ErrorException
class FatalErrorException extends LegacyFatalErrorException
{
public function __construct($message, $code, $severity, $filename, $lineno, $traceOffset = null, $traceArgs = true, array $trace = null, $previous = null)
{

View File

@@ -9,9 +9,49 @@
* file that was distributed with this source code.
*/
namespace Symfony\Component\HttpKernel\Exception;
use Symfony\Component\Debug\Exception\FlattenException as DebugFlattenException;
/**
* FlattenException wraps a PHP Exception to be able to serialize it.
*
* Basically, this class removes all objects from the trace.
*
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated Deprecated in 2.3, to be removed in 3.0. Use the same class from the Debug component instead.
*/
class FlattenException
{
private $handler;
public static function __callStatic($method, $args)
{
if (!method_exists('Symfony\Component\Debug\Exception\FlattenException', $method)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', \get_called_class(), $method));
}
return \call_user_func_array(array('Symfony\Component\Debug\Exception\FlattenException', $method), $args);
}
public function __call($method, $args)
{
if (!isset($this->handler)) {
$this->handler = new DebugFlattenException();
}
if (!method_exists($this->handler, $method)) {
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', \get_class($this), $method));
}
return \call_user_func_array(array($this->handler, $method), $args);
}
}
namespace Symfony\Component\Debug\Exception;
use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
use Symfony\Component\HttpKernel\Exception\FlattenException as LegacyFlattenException;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
@@ -21,7 +61,7 @@ use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class FlattenException
class FlattenException extends LegacyFlattenException
{
private $message;
private $code;
@@ -42,8 +82,6 @@ class FlattenException
if ($exception instanceof HttpExceptionInterface) {
$statusCode = $exception->getStatusCode();
$headers = array_merge($headers, $exception->getHeaders());
} elseif ($exception instanceof RequestExceptionInterface) {
$statusCode = 400;
}
if (null === $statusCode) {
@@ -157,7 +195,7 @@ class FlattenException
return $this->previous;
}
public function setPrevious(self $previous)
public function setPrevious(FlattenException $previous)
{
$this->previous = $previous;
}
@@ -240,10 +278,6 @@ class FlattenException
$result[$key] = array('null', null);
} elseif (\is_bool($value)) {
$result[$key] = array('boolean', $value);
} elseif (\is_int($value)) {
$result[$key] = array('integer', $value);
} elseif (\is_float($value)) {
$result[$key] = array('float', $value);
} elseif (\is_resource($value)) {
$result[$key] = array('resource', get_resource_type($value));
} else {

View File

@@ -1,67 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Debug\Exception;
/**
* Data Object that represents a Silenced Error.
*
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*/
class SilencedErrorContext implements \JsonSerializable
{
public $count = 1;
private $severity;
private $file;
private $line;
private $trace;
public function __construct($severity, $file, $line, array $trace = array(), $count = 1)
{
$this->severity = $severity;
$this->file = $file;
$this->line = $line;
$this->trace = $trace;
$this->count = $count;
}
public function getSeverity()
{
return $this->severity;
}
public function getFile()
{
return $this->file;
}
public function getLine()
{
return $this->line;
}
public function getTrace()
{
return $this->trace;
}
public function JsonSerialize()
{
return array(
'severity' => $this->severity,
'file' => $this->file,
'line' => $this->line,
'trace' => $this->trace,
'count' => $this->count,
);
}
}

File diff suppressed because one or more lines are too long

View File

@@ -13,6 +13,7 @@ namespace Symfony\Component\Debug\FatalErrorHandler;
use Composer\Autoload\ClassLoader as ComposerClassLoader;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\Debug\Exception\ClassNotFoundException;
use Symfony\Component\Debug\Exception\FatalErrorException;
@@ -100,12 +101,17 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
if ($function[0] instanceof DebugClassLoader) {
$function = $function[0]->getClassLoader();
// @deprecated since version 2.5. Returning an object from DebugClassLoader::getClassLoader() is deprecated.
if (\is_object($function)) {
$function = array($function);
}
if (!\is_array($function)) {
continue;
}
}
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader) {
if ($function[0] instanceof ComposerClassLoader || $function[0] instanceof SymfonyClassLoader || $function[0] instanceof SymfonyUniversalClassLoader) {
foreach ($function[0]->getPrefixes() as $prefix => $paths) {
foreach ($paths as $path) {
$classes = array_merge($classes, $this->findClassInPath($path, $class, $prefix));
@@ -201,6 +207,6 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
*/
private function classExists($class)
{
return class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
return class_exists($class, false) || interface_exists($class, false) || (\function_exists('trait_exists') && trait_exists($class, false));
}
}

View File

@@ -26,7 +26,7 @@ class DebugClassLoaderTest extends TestCase
protected function setUp()
{
$this->errorReporting = error_reporting(E_ALL);
$this->errorReporting = error_reporting(E_ALL | E_STRICT);
$this->loader = new ClassLoader();
spl_autoload_register(array($this->loader, 'loadClass'), true, true);
DebugClassLoader::enable();
@@ -125,6 +125,8 @@ class DebugClassLoaderTest extends TestCase
$this->fail('ContextErrorException expected');
} catch (\ErrorException $exception) {
// if an exception is thrown, the test passed
restore_error_handler();
restore_exception_handler();
$this->assertStringStartsWith(__FILE__, $exception->getFile());
if (\PHP_VERSION_ID < 70000) {
$this->assertRegExp('/^Runtime Notice: Declaration/', $exception->getMessage());
@@ -133,9 +135,11 @@ class DebugClassLoaderTest extends TestCase
$this->assertRegExp('/^Warning: Declaration/', $exception->getMessage());
$this->assertEquals(E_WARNING, $exception->getSeverity());
}
} finally {
} catch (\Exception $exception) {
restore_error_handler();
restore_exception_handler();
throw $exception;
}
}
@@ -204,7 +208,7 @@ class DebugClassLoaderTest extends TestCase
$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The "Test\Symfony\Component\Debug\Tests\\'.$class.'" class '.$type.' "Symfony\Component\Debug\Tests\Fixtures\\'.$super.'" that is deprecated but this is a test deprecation notice.',
'message' => 'The Test\Symfony\Component\Debug\Tests\\'.$class.' class '.$type.' Symfony\Component\Debug\Tests\Fixtures\\'.$super.' that is deprecated but this is a test deprecation notice',
);
$this->assertSame($xError, $lastError);
@@ -282,91 +286,11 @@ class DebugClassLoaderTest extends TestCase
$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The "Test\Symfony\Component\Debug\Tests\Float" class uses the reserved name "Float", it will break on PHP 7 and higher',
'message' => 'Test\Symfony\Component\Debug\Tests\Float uses a reserved class name (Float) that will break on PHP 7 and higher',
);
$this->assertSame($xError, $lastError);
}
public function testExtendedFinalClass()
{
set_error_handler(function () { return false; });
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsFinalClass', true);
error_reporting($e);
restore_error_handler();
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass" class is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass".',
);
$this->assertSame($xError, $lastError);
}
public function testExtendedFinalMethod()
{
set_error_handler(function () { return false; });
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true);
error_reporting($e);
restore_error_handler();
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
);
$this->assertSame($xError, $lastError);
}
public function testExtendedDeprecatedMethodDoesntTriggerAnyNotice()
{
set_error_handler(function () { return false; });
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true);
error_reporting($e);
restore_error_handler();
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$this->assertSame(array('type' => E_USER_NOTICE, 'message' => ''), $lastError);
}
public function testInternalsUse()
{
$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true);
error_reporting($e);
restore_error_handler();
$this->assertSame($deprecations, array(
'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class is considered internal since version 3.4. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
'The "Symfony\Component\Debug\Tests\Fixtures\InternalInterface" interface is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternalsParent".',
'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait" trait is considered internal. It may change without further notice. You should not use it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait2::internalMethod()" method is considered internal since version 3.4. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
));
}
}
class ClassLoader
@@ -390,12 +314,16 @@ class ClassLoader
eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
} elseif (__NAMESPACE__.'\Fixtures\CaseMismatch' === $class) {
return $fixtureDir.'CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
return $fixtureDir.'reallyNotPsr0.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
return $fixtureDir.'notPsr0Bis.php';
} elseif (__NAMESPACE__.'\Fixtures\DeprecatedInterface' === $class) {
return $fixtureDir.'DeprecatedInterface.php';
} elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) {
@@ -406,20 +334,6 @@ class ClassLoader
eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsFinalClass' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsFinalClass extends \\'.__NAMESPACE__.'\Fixtures\FinalClass {}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
public function deprecatedMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
public function internalMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
}
}
}

View File

@@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\BufferingLogger;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\Debug\Exception\ContextErrorException;
/**
* ErrorHandlerTest.
@@ -95,33 +95,47 @@ class ErrorHandlerTest extends TestCase
try {
self::triggerNotice($this);
$this->fail('ErrorException expected');
} catch (\ErrorException $exception) {
$this->fail('ContextErrorException expected');
} 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());
if (\PHP_VERSION_ID < 70200) {
$this->assertArrayHasKey('foobar', $exception->getContext());
}
$trace = $exception->getTrace();
$this->assertEquals(__FILE__, $trace[0]['file']);
$this->assertEquals(__CLASS__, $trace[0]['class']);
$this->assertEquals('triggerNotice', $trace[0]['function']);
$this->assertEquals('::', $trace[0]['type']);
$this->assertEquals('Symfony\Component\Debug\ErrorHandler', $trace[0]['class']);
$this->assertEquals('handleError', $trace[0]['function']);
$this->assertEquals('->', $trace[0]['type']);
$this->assertEquals(__FILE__, $trace[0]['file']);
$this->assertEquals(__FILE__, $trace[1]['file']);
$this->assertEquals(__CLASS__, $trace[1]['class']);
$this->assertEquals(__FUNCTION__, $trace[1]['function']);
$this->assertEquals('->', $trace[1]['type']);
} finally {
$this->assertEquals('triggerNotice', $trace[1]['function']);
$this->assertEquals('::', $trace[1]['type']);
$this->assertEquals(__FILE__, $trace[1]['file']);
$this->assertEquals(__CLASS__, $trace[2]['class']);
$this->assertEquals(__FUNCTION__, $trace[2]['function']);
$this->assertEquals('->', $trace[2]['type']);
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
// dummy function to test trace in error handler.
private static function triggerNotice($that)
{
// dummy variable to check for in error handler.
$foobar = 123;
$that->assertSame('', $foo.$foo.$bar);
}
@@ -131,9 +145,14 @@ class ErrorHandlerTest extends TestCase
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertEquals(3 | E_RECOVERABLE_ERROR | E_USER_ERROR, $handler->throwAt(0));
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -165,9 +184,14 @@ class ErrorHandlerTest extends TestCase
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
);
$this->assertSame($loggers, $handler->setLoggers(array()));
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -218,14 +242,14 @@ class ErrorHandlerTest extends TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$warnArgCheck = function ($logLevel, $message, $context) {
$this->assertEquals('info', $logLevel);
$this->assertEquals('User Deprecated: foo', $message);
$this->assertArrayHasKey('exception', $context);
$exception = $context['exception'];
$this->assertInstanceOf(\ErrorException::class, $exception);
$this->assertSame('User Deprecated: foo', $exception->getMessage());
$this->assertSame(E_USER_DEPRECATED, $exception->getSeverity());
$that = $this;
$warnArgCheck = function ($logLevel, $message, $context) use ($that) {
$that->assertEquals('info', $logLevel);
$that->assertEquals('foo', $message);
$that->assertArrayHasKey('type', $context);
$that->assertEquals($context['type'], E_USER_DEPRECATED);
$that->assertArrayHasKey('stack', $context);
$that->assertInternalType('array', $context['stack']);
};
$logger
@@ -243,17 +267,11 @@ class ErrorHandlerTest extends TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$line = null;
$logArgCheck = function ($level, $message, $context) use (&$line) {
$this->assertEquals('Notice: Undefined variable: undefVar', $message);
$this->assertArrayHasKey('exception', $context);
$exception = $context['exception'];
$this->assertInstanceOf(SilencedErrorContext::class, $exception);
$this->assertSame(E_NOTICE, $exception->getSeverity());
$this->assertSame(__FILE__, $exception->getFile());
$this->assertSame($line, $exception->getLine());
$this->assertNotEmpty($exception->getTrace());
$this->assertSame(1, $exception->count);
$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
@@ -266,7 +284,6 @@ class ErrorHandlerTest extends TestCase
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->screamAt(E_NOTICE);
unset($undefVar);
$line = __LINE__ + 1;
@$undefVar++;
restore_error_handler();
@@ -295,20 +312,25 @@ class ErrorHandlerTest extends TestCase
}
$this->assertSame($x, $e);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
public function testHandleDeprecation()
{
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals(LogLevel::INFO, $level);
$this->assertArrayHasKey('exception', $context);
$exception = $context['exception'];
$this->assertInstanceOf(\ErrorException::class, $exception);
$this->assertSame('User Deprecated: Foo deprecation', $exception->getMessage());
$that = $this;
$logArgCheck = function ($level, $message, $context) use ($that) {
$that->assertEquals(LogLevel::INFO, $level);
$that->assertArrayHasKey('level', $context);
$that->assertEquals(E_RECOVERABLE_ERROR | E_USER_ERROR | E_DEPRECATED | E_USER_DEPRECATED, $context['level']);
$that->assertArrayHasKey('stack', $context);
};
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
@@ -335,10 +357,11 @@ class ErrorHandlerTest extends TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertSame('Uncaught Exception: foo', $message);
$this->assertArrayHasKey('exception', $context);
$this->assertInstanceOf(\Exception::class, $context['exception']);
$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
@@ -356,20 +379,23 @@ class ErrorHandlerTest extends TestCase
$this->assertSame($exception, $e);
}
$handler->setExceptionHandler(function ($e) use ($exception) {
$this->assertSame($exception, $e);
$that = $this;
$handler->setExceptionHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handleException($exception);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
/**
* @group legacy
*/
public function testErrorStacking()
{
try {
@@ -383,7 +409,7 @@ class ErrorHandlerTest extends TestCase
->method('log')
->withConsecutive(
array($this->equalTo(LogLevel::WARNING), $this->equalTo('Dummy log')),
array($this->equalTo(LogLevel::DEBUG), $this->equalTo('User Warning: Silenced warning'))
array($this->equalTo(LogLevel::DEBUG), $this->equalTo('Silenced warning'))
)
;
@@ -393,9 +419,14 @@ class ErrorHandlerTest extends TestCase
@trigger_error('Silenced warning', E_USER_WARNING);
$logger->log(LogLevel::WARNING, 'Dummy log');
ErrorHandler::unstackErrors();
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
@@ -425,53 +456,23 @@ class ErrorHandlerTest extends TestCase
$this->assertSame($loggers, $handler->setLoggers(array()));
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
$expectedLog = array(LogLevel::INFO, 'Foo message', array('type' => E_DEPRECATED, 'file' => __FILE__, 'line' => 123, 'level' => error_reporting()));
$logs = $bootLogger->cleanLogs();
unset($logs[0][2]['stack']);
$this->assertCount(1, $logs);
$log = $logs[0];
$this->assertSame('info', $log[0]);
$this->assertSame('Deprecated: Foo message', $log[1]);
$this->assertArrayHasKey('exception', $log[2]);
$exception = $log[2]['exception'];
$this->assertInstanceOf(\ErrorException::class, $exception);
$this->assertSame('Deprecated: Foo message', $exception->getMessage());
$this->assertSame(__FILE__, $exception->getFile());
$this->assertSame(123, $exception->getLine());
$this->assertSame(E_DEPRECATED, $exception->getSeverity());
$this->assertSame(array($expectedLog), $logs);
$bootLogger->log(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
$bootLogger->log($expectedLog[0], $expectedLog[1], $expectedLog[2]);
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$mockLogger->expects($this->once())
->method('log')
->with(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
->with(LogLevel::WARNING, 'Foo message', $expectedLog[2]);
$handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
}
/**
* @group no-hhvm
*/
public function testSettingLoggerWhenExceptionIsBuffered()
{
$bootLogger = new BufferingLogger();
$handler = new ErrorHandler($bootLogger);
$exception = new \Exception('Foo message');
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$mockLogger->expects($this->once())
->method('log')
->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', array('exception' => $exception));
$handler->setExceptionHandler(function () use ($handler, $mockLogger) {
$handler->setDefaultLogger($mockLogger);
});
$handler->handleException($exception);
}
/**
* @group no-hhvm
*/
@@ -489,10 +490,11 @@ class ErrorHandlerTest extends TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$logArgCheck = function ($level, $message, $context) {
$this->assertEquals('Fatal Parse Error: foo', $message);
$this->assertArrayHasKey('exception', $context);
$this->assertInstanceOf(\Exception::class, $context['exception']);
$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_PARSE);
};
$logger
@@ -547,7 +549,14 @@ class ErrorHandlerTest extends TestCase
->method('log')
->with(
$this->equalTo(LogLevel::CRITICAL),
$this->equalTo('Fatal Error: foo')
$this->equalTo('Fatal Error: foo'),
$this->equalTo(array(
'type' => 1,
'file' => 'bar',
'line' => 123,
'level' => -1,
'stack' => array(456),
))
)
;
@@ -564,9 +573,56 @@ class ErrorHandlerTest extends TestCase
\call_user_func_array(array($handler, 'handleError'), $error);
$handler->handleFatalError($error);
} finally {
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}
/**
* @group legacy
*/
public function testLegacyInterface()
{
try {
$handler = ErrorHandler::register(0);
$this->assertFalse($handler->handle(0, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$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->setLogger($logger, 'scream');
unset($undefVar);
@$undefVar++;
restore_error_handler();
restore_exception_handler();
} catch (\Exception $e) {
restore_error_handler();
restore_exception_handler();
throw $e;
}
}

View File

@@ -13,7 +13,6 @@ namespace Symfony\Component\Debug\Tests\Exception;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
@@ -80,11 +79,6 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create(new UnsupportedMediaTypeHttpException());
$this->assertEquals('415', $flattened->getStatusCode());
if (class_exists(SuspiciousOperationException::class)) {
$flattened = FlattenException::create(new SuspiciousOperationException());
$this->assertEquals('400', $flattened->getStatusCode());
}
}
public function testHeadersForHttpException()
@@ -197,68 +191,6 @@ class FlattenExceptionTest extends TestCase
);
}
public function testArguments()
{
$dh = opendir(__DIR__);
$fh = tmpfile();
$incomplete = unserialize('O:14:"BogusTestClass":0:{}');
$exception = $this->createException(array(
(object) array('foo' => 1),
new NotFoundHttpException(),
$incomplete,
$dh,
$fh,
function () {},
array(1, 2),
array('foo' => 123),
null,
true,
false,
0,
0.0,
'0',
'',
INF,
NAN,
));
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
$args = $trace[1]['args'];
$array = $args[0][1];
closedir($dh);
fclose($fh);
$i = 0;
$this->assertSame(array('object', 'stdClass'), $array[$i++]);
$this->assertSame(array('object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'), $array[$i++]);
$this->assertSame(array('incomplete-object', 'BogusTestClass'), $array[$i++]);
$this->assertSame(array('resource', \defined('HHVM_VERSION') ? 'Directory' : 'stream'), $array[$i++]);
$this->assertSame(array('resource', 'stream'), $array[$i++]);
$args = $array[$i++];
$this->assertSame($args[0], 'object');
$this->assertTrue('Closure' === $args[1] || is_subclass_of($args[1], '\Closure'), 'Expect object class name to be Closure or a subclass of Closure.');
$this->assertSame(array('array', array(array('integer', 1), array('integer', 2))), $array[$i++]);
$this->assertSame(array('array', array('foo' => array('integer', 123))), $array[$i++]);
$this->assertSame(array('null', null), $array[$i++]);
$this->assertSame(array('boolean', true), $array[$i++]);
$this->assertSame(array('boolean', false), $array[$i++]);
$this->assertSame(array('integer', 0), $array[$i++]);
$this->assertSame(array('float', 0.0), $array[$i++]);
$this->assertSame(array('string', '0'), $array[$i++]);
$this->assertSame(array('string', ''), $array[$i++]);
$this->assertSame(array('float', INF), $array[$i++]);
// assertEquals() does not like NAN values.
$this->assertEquals($array[$i][0], 'float');
$this->assertTrue(is_nan($array[$i++][1]));
}
public function testRecursionInArguments()
{
$a = null;
@@ -286,9 +218,6 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
$this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*')));
$serializeTrace = serialize($trace);
$this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
@@ -299,4 +228,45 @@ class FlattenExceptionTest extends TestCase
{
return new \Exception();
}
public function testSetTraceIncompleteClass()
{
$flattened = FlattenException::create(new \Exception('test', 123));
$flattened->setTrace(
array(
array(
'file' => __FILE__,
'line' => 123,
'function' => 'test',
'args' => array(
unserialize('O:14:"BogusTestClass":0:{}'),
),
),
),
'foo.php', 123
);
$this->assertEquals(array(
array(
'message' => 'test',
'class' => 'Exception',
'trace' => array(
array(
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '',
'file' => 'foo.php', 'line' => 123,
'args' => array(),
),
array(
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => 'test',
'file' => __FILE__, 'line' => 123,
'args' => array(
array(
'incomplete-object', 'BogusTestClass',
),
),
),
),
),
), $flattened->toArray());
}
}

View File

@@ -39,8 +39,8 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo'));
$response = ob_get_clean();
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertNotContains('<div class="trace trace-as-html">', $response);
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response);
$this->assertNotContains('<h2 class="block_exception clear_fix">', $response);
$handler = new ExceptionHandler(true);
@@ -48,8 +48,8 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo'));
$response = ob_get_clean();
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertContains('<div class="trace trace-as-html">', $response);
$this->assertContains('<h1>Whoops, looks like something went wrong.</h1>', $response);
$this->assertContains('<h2 class="block_exception clear_fix">', $response);
}
public function testStatusCode()
@@ -94,7 +94,7 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo', 0, new \RuntimeException('Bar')));
$response = ob_get_clean();
$this->assertStringMatchesFormat('%A<p class="break-long-words trace-message">Foo</p>%A<p class="break-long-words trace-message">Bar</p>%A', $response);
$this->assertStringMatchesFormat('%A<span class="exception_message">Foo</span>%A<span class="exception_message">Bar</span>%A', $response);
}
public function testHandle()
@@ -108,8 +108,9 @@ class ExceptionHandlerTest extends TestCase
$handler->handle($exception);
$handler->setHandler(function ($e) use ($exception) {
$this->assertSame($exception, $e);
$that = $this;
$handler->setHandler(function ($e) use ($exception, $that) {
$that->assertSame($exception, $e);
});
$handler->handle($exception);
@@ -124,8 +125,9 @@ class ExceptionHandlerTest extends TestCase
->expects($this->once())
->method('sendPhpResponse');
$handler->setHandler(function ($e) {
$this->fail('OutOfMemoryException should bypass the handler');
$that = $this;
$handler->setHandler(function ($e) use ($that) {
$that->fail('OutOfMemoryException should bypass the handler');
});
$handler->handle($exception);

View File

@@ -13,6 +13,8 @@ namespace Symfony\Component\Debug\Tests\FatalErrorHandler;
use Composer\Autoload\ClassLoader as ComposerClassLoader;
use PHPUnit\Framework\TestCase;
use Symfony\Component\ClassLoader\ClassLoader as SymfonyClassLoader;
use Symfony\Component\ClassLoader\UniversalClassLoader as SymfonyUniversalClassLoader;
use Symfony\Component\Debug\DebugClassLoader;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -67,12 +69,35 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
$this->assertSame($error['line'], $exception->getLine());
}
/**
* @group legacy
*/
public function testLegacyHandleClassNotFound()
{
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
$symfonyUniversalClassLoader = new SymfonyUniversalClassLoader();
$symfonyUniversalClassLoader->registerPrefixes($prefixes);
$this->testHandleClassNotFound(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
),
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
array($symfonyUniversalClassLoader, 'loadClass')
);
}
public function provideClassNotFoundData()
{
$autoloader = new ComposerClassLoader();
$autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception'));
$prefixes = array('Symfony\Component\Debug\Exception\\' => realpath(__DIR__.'/../../Exception'));
$debugClassLoader = new DebugClassLoader(array($autoloader, 'loadClass'));
$symfonyAutoloader = new SymfonyClassLoader();
$symfonyAutoloader->addPrefixes($prefixes);
$debugClassLoader = new DebugClassLoader(array($symfonyAutoloader, 'loadClass'));
return array(
array(
@@ -128,7 +153,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
'message' => 'Class \'Foo\\Bar\\UndefinedFunctionException\' not found',
),
"Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\Bar\".\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
array($autoloader, 'loadClass'),
array($symfonyAutoloader, 'loadClass'),
),
array(
array(

View File

@@ -64,10 +64,10 @@ class UndefinedMethodFatalErrorHandlerTest extends TestCase
),
array(
array(
'type' => 1,
'message' => 'Call to undefined method class@anonymous::test()',
'file' => '/home/possum/work/symfony/test.php',
'line' => 11,
'type' => 1,
'message' => 'Call to undefined method class@anonymous::test()',
'file' => '/home/possum/work/symfony/test.php',
'line' => 11,
),
'Attempted to call an undefined method named "test" of class "class@anonymous".',
),

View File

@@ -1,13 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class AnnotatedClass
{
/**
* @deprecated since version 3.4.
*/
public function deprecatedMethod()
{
}
}

View File

@@ -1,17 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class ExtendedFinalMethod extends FinalMethod
{
/**
* {@inheritdoc}
*/
public function finalMethod()
{
}
public function anotherMethod()
{
}
}

View File

@@ -1,10 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @final since version 3.3.
*/
class FinalClass
{
}

View File

@@ -1,17 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class FinalMethod
{
/**
* @final since version 3.3.
*/
public function finalMethod()
{
}
public function anotherMethod()
{
}
}

View File

@@ -1,15 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @internal since version 3.4.
*/
class InternalClass
{
use InternalTrait2;
public function usedInInternalClass()
{
}
}

View File

@@ -1,10 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @internal
*/
interface InternalInterface
{
}

View File

@@ -1,10 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @internal
*/
trait InternalTrait
{
}

View File

@@ -1,23 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @internal
*/
trait InternalTrait2
{
/**
* @internal since version 3.4
*/
public function internalMethod()
{
}
/**
* @internal but should not trigger a deprecation
*/
public function usedInInternalClass()
{
}
}

View File

@@ -1,26 +0,0 @@
--TEST--
Test DebugClassLoader with previously loaded parents
--FILE--
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
use Symfony\Component\Debug\DebugClassLoader;
$vendor = __DIR__;
while (!file_exists($vendor.'/vendor')) {
$vendor = \dirname($vendor);
}
require $vendor.'/vendor/autoload.php';
class_exists(FinalMethod::class);
set_error_handler(function ($type, $msg) { echo $msg, "\n"; });
DebugClassLoader::enable();
class_exists(ExtendedFinalMethod::class);
?>
--EXPECTF--
The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final since version 3.3. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".

View File

@@ -1,5 +1,7 @@
--TEST--
Test catching fatal errors when handlers are nested
--INI--
display_errors=0
--FILE--
<?php
@@ -24,7 +26,6 @@ if (true) {
?>
--EXPECTF--
Fatal error: Class 'Symfony\Component\Debug\missing' not found in %s on line %d
object(Symfony\Component\Debug\Exception\ClassNotFoundException)#%d (8) {
["message":protected]=>
string(131) "Attempted to load class "missing" from namespace "Symfony\Component\Debug".

View File

@@ -16,14 +16,15 @@
}
],
"require": {
"php": "^5.5.9|>=7.0.8",
"php": ">=5.3.9",
"psr/log": "~1.0"
},
"conflict": {
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
},
"require-dev": {
"symfony/http-kernel": "~2.8|~3.0|~4.0"
"symfony/class-loader": "~2.2|~3.0.0",
"symfony/http-kernel": "~2.3.24|~2.5.9|^2.6.2|~3.0.0"
},
"autoload": {
"psr-4": { "Symfony\\Component\\Debug\\": "" },
@@ -34,7 +35,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "3.4-dev"
"dev-master": "2.8-dev"
}
}
}

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"