Compare commits

..

1 Commits

Author SHA1 Message Date
Nicolas Grekas
bbfbd42785 minor #28114 [travis] merge "same Symfony version" jobs in one (nicolas-grekas)
This PR was merged into the 2.8 branch.

Discussion
----------

[travis] merge "same Symfony version" jobs in one

| Q             | A
| ------------- | ---
| Branch?       | 2.8
| Bug fix?      | no
| New feature?  |
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

Allowing to consume fewer jobs and save the 1 to 2 minutes bootstrap time of workers.

Commits
-------

9857ca07aa [travis] merge "same Symfony version" jobs in one
2018-08-03 13:27:18 +02:00
46 changed files with 593 additions and 1502 deletions

View File

@@ -20,17 +20,17 @@ use Psr\Log\AbstractLogger;
*/
class BufferingLogger extends AbstractLogger
{
private $logs = [];
private $logs = array();
public function log($level, $message, array $context = [])
public function log($level, $message, array $context = array())
{
$this->logs[] = [$level, $message, $context];
$this->logs[] = array($level, $message, $context);
}
public function cleanLogs()
{
$logs = $this->logs;
$this->logs = [];
$this->logs = array();
return $logs;
}

View File

@@ -1,14 +1,6 @@
CHANGELOG
=========
4.3.0
-----
* made the `ErrorHandler` and `ExceptionHandler` classes final
* added `Exception\FlattenException::getAsString` and
`Exception\FlattenException::getTraceAsString` to increase compatibility to php
exception objects
4.0.0
-----

View File

@@ -42,10 +42,10 @@ class Debug
error_reporting(E_ALL);
}
if (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
if (!\in_array(\PHP_SAPI, array('cli', 'phpdbg'), true)) {
ini_set('display_errors', 0);
ExceptionHandler::register();
} elseif ($displayErrors && (!filter_var(ini_get('log_errors'), FILTER_VALIDATE_BOOLEAN) || ini_get('error_log'))) {
} 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);
}

View File

@@ -11,8 +11,6 @@
namespace Symfony\Component\Debug;
use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation;
/**
* Autoloader checking if the class is really defined in the file found.
*
@@ -23,23 +21,20 @@ use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation;
* @author Fabien Potencier <fabien@symfony.com>
* @author Christophe Coevoet <stof@notk.org>
* @author Nicolas Grekas <p@tchwork.com>
* @author Guilhem Niot <guilhem.niot@gmail.com>
*/
class DebugClassLoader
{
private $classLoader;
private $isFinder;
private $loaded = [];
private $loaded = array();
private static $caseCheck;
private static $checkedClasses = [];
private static $final = [];
private static $finalMethods = [];
private static $deprecated = [];
private static $internal = [];
private static $internalMethods = [];
private static $annotatedParameters = [];
private static $darwinCache = ['/' => ['/', []]];
private static $method = [];
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 $darwinCache = array('/' => array('/', array()));
public function __construct(callable $classLoader)
{
@@ -99,7 +94,7 @@ class DebugClassLoader
foreach ($functions as $function) {
if (!\is_array($function) || !$function[0] instanceof self) {
$function = [new static($function), 'loadClass'];
$function = array(new static($function), 'loadClass');
}
spl_autoload_register($function);
@@ -128,19 +123,13 @@ class DebugClassLoader
}
}
/**
* @return string|null
*/
public function findFile($class)
{
return $this->isFinder ? $this->classLoader[0]->findFile($class) ?: null : null;
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
*
* @return bool|null True, if loaded
*
* @throws \RuntimeException
*/
public function loadClass($class)
@@ -150,17 +139,17 @@ class DebugClassLoader
try {
if ($this->isFinder && !isset($this->loaded[$class])) {
$this->loaded[$class] = true;
if (!$file = $this->classLoader[0]->findFile($class) ?: false) {
// no-op
} elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) {
if ($file = $this->classLoader[0]->findFile($class) ?: false) {
$wasCached = \function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file);
require $file;
return;
} else {
require $file;
if ($wasCached) {
return;
}
}
} else {
($this->classLoader)($class);
\call_user_func($this->classLoader, $class);
$file = false;
}
} finally {
@@ -194,306 +183,196 @@ class DebugClassLoader
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
}
$deprecations = $this->checkAnnotations($refl, $name);
foreach ($deprecations as $message) {
@trigger_error($message, E_USER_DEPRECATED);
}
}
if (!$file) {
return;
}
if (!$exists) {
if (false !== strpos($class, '/')) {
throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
}
throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
}
if (self::$caseCheck && $message = $this->checkCase($refl, $file, $class)) {
throw new \RuntimeException(sprintf('Case mismatch between class and real file names: "%s" vs "%s" in "%s".', $message[0], $message[1], $message[2]));
}
}
public function checkAnnotations(\ReflectionClass $refl, $class)
{
$deprecations = [];
// Don't trigger deprecations for classes in the same vendor
if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) {
$len = 0;
$ns = '';
} else {
$ns = \str_replace('_', '\\', \substr($class, 0, $len));
}
// Detect annotations on the class
if (false !== $doc = $refl->getDocComment()) {
foreach (['final', 'deprecated', 'internal'] as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
self::${$annotation}[$class] = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
}
}
if ($refl->isInterface() && false !== \strpos($doc, 'method') && preg_match_all('#\n \* @method\s+(static\s+)?+(?:[\w\|&\[\]\\\]+\s+)?(\w+(?:\s*\([^\)]*\))?)+(.+?([[:punct:]]\s*)?)?(?=\r?\n \*(?: @|/$|\r?\n))#', $doc, $notice, PREG_SET_ORDER)) {
foreach ($notice as $method) {
$static = '' !== $method[1];
$name = $method[2];
$description = $method[3] ?? null;
if (false === strpos($name, '(')) {
$name .= '()';
}
if (null !== $description) {
$description = trim($description);
if (!isset($method[4])) {
$description .= '.';
}
}
self::$method[$class][] = [$class, $name, $static, $description];
}
}
}
$parent = \get_parent_class($class);
$parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent);
if ($parent) {
$parentAndOwnInterfaces[$parent] = $parent;
if (!isset(self::$checkedClasses[$parent])) {
$this->checkClass($parent);
}
if (isset(self::$final[$parent])) {
$deprecations[] = 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], $class);
}
}
// Detect if the parent is annotated
foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) {
if (!isset(self::$checkedClasses[$use])) {
$this->checkClass($use);
}
if (isset(self::$deprecated[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len) && !isset(self::$deprecated[$class])) {
$type = class_exists($class, false) ? 'class' : (interface_exists($class, false) ? 'interface' : 'trait');
$verb = class_exists($use, false) || interface_exists($class, false) ? 'extends' : (interface_exists($use, false) ? 'implements' : 'uses');
$deprecations[] = sprintf('The "%s" %s %s "%s" that is deprecated%s.', $class, $type, $verb, $use, self::$deprecated[$use]);
}
if (isset(self::$internal[$use]) && \strncmp($ns, \str_replace('_', '\\', $use), $len)) {
$deprecations[] = 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], $class);
}
if (isset(self::$method[$use])) {
if ($refl->isAbstract()) {
if (isset(self::$method[$class])) {
self::$method[$class] = array_merge(self::$method[$class], self::$method[$use]);
} else {
self::$method[$class] = self::$method[$use];
}
} elseif (!$refl->isInterface()) {
$hasCall = $refl->hasMethod('__call');
$hasStaticCall = $refl->hasMethod('__callStatic');
foreach (self::$method[$use] as $method) {
list($interface, $name, $static, $description) = $method;
if ($static ? $hasStaticCall : $hasCall) {
continue;
}
$realName = substr($name, 0, strpos($name, '('));
if (!$refl->hasMethod($realName) || !($methodRefl = $refl->getMethod($realName))->isPublic() || ($static && !$methodRefl->isStatic()) || (!$static && $methodRefl->isStatic())) {
$deprecations[] = sprintf('Class "%s" should implement method "%s::%s"%s', $class, ($static ? 'static ' : '').$interface, $name, null == $description ? '.' : ': '.$description);
}
}
}
}
}
if (\trait_exists($class)) {
return $deprecations;
}
// Inherit @final, @internal and @param annotations for methods
self::$finalMethods[$class] = [];
self::$internalMethods[$class] = [];
self::$annotatedParameters[$class] = [];
foreach ($parentAndOwnInterfaces as $use) {
foreach (['finalMethods', 'internalMethods', 'annotatedParameters'] as $property) {
if (isset(self::${$property}[$use])) {
self::${$property}[$class] = self::${$property}[$class] ? self::${$property}[$use] + self::${$property}[$class] : self::${$property}[$use];
}
}
}
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
if ($method->class !== $class) {
continue;
}
if ($parent && isset(self::$finalMethods[$parent][$method->name])) {
list($declaringClass, $message) = self::$finalMethods[$parent][$method->name];
$deprecations[] = 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, $class);
}
if (isset(self::$internalMethods[$class][$method->name])) {
list($declaringClass, $message) = self::$internalMethods[$class][$method->name];
if (\strncmp($ns, $declaringClass, $len)) {
$deprecations[] = 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, $class);
}
}
// To read method annotations
$doc = $method->getDocComment();
if (isset(self::$annotatedParameters[$class][$method->name])) {
$definedParameters = [];
foreach ($method->getParameters() as $parameter) {
$definedParameters[$parameter->name] = true;
}
foreach (self::$annotatedParameters[$class][$method->name] as $parameterName => $deprecation) {
if (!isset($definedParameters[$parameterName]) && !($doc && preg_match("/\\n\\s+\\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\\\${$parameterName}\\b/", $doc))) {
$deprecations[] = sprintf($deprecation, $class);
}
}
}
if (!$doc) {
continue;
}
$finalOrInternal = false;
foreach (['final', 'internal'] as $annotation) {
if (false !== \strpos($doc, $annotation) && preg_match('#\n\s+\* @'.$annotation.'(?:( .+?)\.?)?\r?\n\s+\*(?: @|/$|\r?\n)#s', $doc, $notice)) {
$message = isset($notice[1]) ? preg_replace('#\.?\r?\n( \*)? *(?= |\r?\n|$)#', '', $notice[1]) : '';
self::${$annotation.'Methods'}[$class][$method->name] = [$class, $message];
$finalOrInternal = true;
}
}
if ($finalOrInternal || $method->isConstructor() || false === \strpos($doc, '@param') || StatelessInvocation::class === $class) {
continue;
}
if (!preg_match_all('#\n\s+\* @param +((?(?!callable *\().*?|callable *\(.*\).*?))(?<= )\$([a-zA-Z0-9_\x7f-\xff]++)#', $doc, $matches, PREG_SET_ORDER)) {
continue;
}
if (!isset(self::$annotatedParameters[$class][$method->name])) {
$definedParameters = [];
foreach ($method->getParameters() as $parameter) {
$definedParameters[$parameter->name] = true;
}
}
foreach ($matches as list(, $parameterType, $parameterName)) {
if (!isset($definedParameters[$parameterName])) {
$parameterType = trim($parameterType);
self::$annotatedParameters[$class][$method->name][$parameterName] = sprintf('The "%%s::%s()" method will require a new "%s$%s" argument in the next major version of its parent class "%s", not defining it is deprecated.', $method->name, $parameterType ? $parameterType.' ' : '', $parameterName, $method->class);
}
}
}
return $deprecations;
}
public function checkCase(\ReflectionClass $refl, $file, $class)
{
$real = explode('\\', $class.strrchr($file, '.'));
$tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
$i = \count($tail) - 1;
$j = \count($real) - 1;
while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
--$i;
--$j;
}
array_splice($tail, 0, $i + 1);
if (!$tail) {
return;
}
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
$tailLen = \strlen($tail);
$real = $refl->getFileName();
if (2 === self::$caseCheck) {
$real = $this->darwinRealpath($real);
}
if (0 === substr_compare($real, $tail, -$tailLen, $tailLen, true)
&& 0 !== substr_compare($real, $tail, -$tailLen, $tailLen, false)
) {
return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)];
}
}
/**
* `realpath` on MacOSX doesn't normalize the case of characters.
*/
private function darwinRealpath($real)
{
$i = 1 + strrpos($real, '/');
$file = substr($real, $i);
$real = substr($real, 0, $i);
if (isset(self::$darwinCache[$real])) {
$kDir = $real;
} else {
$kDir = strtolower($real);
if (isset(self::$darwinCache[$kDir])) {
$real = self::$darwinCache[$kDir][0];
// Don't trigger deprecations for classes in the same vendor
if (2 > $len = 1 + (\strpos($name, '\\') ?: \strpos($name, '_'))) {
$len = 0;
$ns = '';
} else {
$dir = getcwd();
chdir($real);
$real = getcwd().'/';
chdir($dir);
$dir = $real;
$k = $kDir;
$i = \strlen($dir) - 1;
while (!isset(self::$darwinCache[$k])) {
self::$darwinCache[$k] = [$dir, []];
self::$darwinCache[$dir] = &self::$darwinCache[$k];
while ('/' !== $dir[--$i]) {
}
$k = substr($k, 0, ++$i);
$dir = substr($dir, 0, $i--);
}
$ns = \substr($name, 0, $len);
}
}
$dirFiles = self::$darwinCache[$kDir][1];
if (!isset($dirFiles[$file]) && ') : eval()\'d code' === substr($file, -17)) {
// Get the file name from "file_name.php(123) : eval()'d code"
$file = substr($file, 0, strrpos($file, '(', -17));
}
if (isset($dirFiles[$file])) {
return $real .= $dirFiles[$file];
}
$kFile = strtolower($file);
if (!isset($dirFiles[$kFile])) {
foreach (scandir($real, 2) as $f) {
if ('.' !== $f[0]) {
$dirFiles[$f] = $f;
if ($f === $file) {
$kFile = $k = $file;
} elseif ($f !== $k = strtolower($f)) {
$dirFiles[$k] = $f;
// 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]) : '';
}
}
}
$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];
}
}
}
$isClass = \class_exists($name, false);
foreach ($refl->getMethods(\ReflectionMethod::IS_PUBLIC | \ReflectionMethod::IS_PROTECTED) as $method) {
if ($method->class !== $name) {
continue;
}
// 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);
}
}
}
// 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);
}
}
}
self::$darwinCache[$kDir][1] = $dirFiles;
}
return $real .= $dirFiles[$kFile];
if ($file) {
if (!$exists) {
if (false !== strpos($class, '/')) {
throw new \RuntimeException(sprintf('Trying to autoload a class with an invalid name "%s". Be careful that the namespace separator is "\" in PHP, not "/".', $class));
}
throw new \RuntimeException(sprintf('The autoloader expected class "%s" to be defined in file "%s". The file was found but the class was not in it, the class name or namespace probably has a typo.', $class, $file));
}
if (self::$caseCheck) {
$real = explode('\\', $class.strrchr($file, '.'));
$tail = explode(\DIRECTORY_SEPARATOR, str_replace('/', \DIRECTORY_SEPARATOR, $file));
$i = \count($tail) - 1;
$j = \count($real) - 1;
while (isset($tail[$i], $real[$j]) && $tail[$i] === $real[$j]) {
--$i;
--$j;
}
array_splice($tail, 0, $i + 1);
}
if (self::$caseCheck && $tail) {
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
$tailLen = \strlen($tail);
$real = $refl->getFileName();
if (2 === self::$caseCheck) {
// realpath() on MacOSX doesn't normalize the case of characters
$i = 1 + strrpos($real, '/');
$file = substr($real, $i);
$real = substr($real, 0, $i);
if (isset(self::$darwinCache[$real])) {
$kDir = $real;
} else {
$kDir = strtolower($real);
if (isset(self::$darwinCache[$kDir])) {
$real = self::$darwinCache[$kDir][0];
} else {
$dir = getcwd();
chdir($real);
$real = getcwd().'/';
chdir($dir);
$dir = $real;
$k = $kDir;
$i = \strlen($dir) - 1;
while (!isset(self::$darwinCache[$k])) {
self::$darwinCache[$k] = array($dir, array());
self::$darwinCache[$dir] = &self::$darwinCache[$k];
while ('/' !== $dir[--$i]) {
}
$k = substr($k, 0, ++$i);
$dir = substr($dir, 0, $i--);
}
}
}
$dirFiles = self::$darwinCache[$kDir][1];
if (isset($dirFiles[$file])) {
$kFile = $file;
} else {
$kFile = strtolower($file);
if (!isset($dirFiles[$kFile])) {
foreach (scandir($real, 2) as $f) {
if ('.' !== $f[0]) {
$dirFiles[$f] = $f;
if ($f === $file) {
$kFile = $k = $file;
} elseif ($f !== $k = strtolower($f)) {
$dirFiles[$k] = $f;
}
}
}
self::$darwinCache[$kDir][1] = $dirFiles;
}
}
$real .= $dirFiles[$kFile];
}
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)));
}
}
}
}
/**

View File

@@ -15,7 +15,6 @@ use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\Debug\Exception\OutOfMemoryException;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
@@ -45,12 +44,10 @@ use Symfony\Component\Debug\FatalErrorHandler\UndefinedMethodFatalErrorHandler;
*
* @author Nicolas Grekas <p@tchwork.com>
* @author Grégoire Pineau <lyrixx@lyrixx.info>
*
* @final since Symfony 4.3
*/
class ErrorHandler
{
private $levels = [
private $levels = array(
E_DEPRECATED => 'Deprecated',
E_USER_DEPRECATED => 'User Deprecated',
E_NOTICE => 'Notice',
@@ -66,25 +63,25 @@ class ErrorHandler
E_PARSE => 'Parse Error',
E_ERROR => 'Error',
E_CORE_ERROR => 'Core Error',
];
);
private $loggers = [
E_DEPRECATED => [null, LogLevel::INFO],
E_USER_DEPRECATED => [null, LogLevel::INFO],
E_NOTICE => [null, LogLevel::WARNING],
E_USER_NOTICE => [null, LogLevel::WARNING],
E_STRICT => [null, LogLevel::WARNING],
E_WARNING => [null, LogLevel::WARNING],
E_USER_WARNING => [null, LogLevel::WARNING],
E_COMPILE_WARNING => [null, LogLevel::WARNING],
E_CORE_WARNING => [null, LogLevel::WARNING],
E_USER_ERROR => [null, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
E_PARSE => [null, LogLevel::CRITICAL],
E_ERROR => [null, LogLevel::CRITICAL],
E_CORE_ERROR => [null, LogLevel::CRITICAL],
];
private $loggers = array(
E_DEPRECATED => array(null, LogLevel::INFO),
E_USER_DEPRECATED => array(null, LogLevel::INFO),
E_NOTICE => array(null, LogLevel::WARNING),
E_USER_NOTICE => array(null, LogLevel::WARNING),
E_STRICT => array(null, LogLevel::WARNING),
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::CRITICAL),
E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
E_PARSE => array(null, LogLevel::CRITICAL),
E_ERROR => array(null, LogLevel::CRITICAL),
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
);
private $thrownErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
private $scopedErrors = 0x1FFF; // E_ALL - E_DEPRECATED - E_USER_DEPRECATED
@@ -100,7 +97,7 @@ class ErrorHandler
private static $reservedMemory;
private static $toStringException = null;
private static $silencedErrorCache = [];
private static $silencedErrorCache = array();
private static $silencedErrorCount = 0;
private static $exitCode = 0;
@@ -123,10 +120,10 @@ class ErrorHandler
$handler = new static();
}
if (null === $prev = set_error_handler([$handler, 'handleError'])) {
if (null === $prev = set_error_handler(array($handler, 'handleError'))) {
restore_error_handler();
// Specifying the error types earlier would expose us to https://bugs.php.net/63206
set_error_handler([$handler, 'handleError'], $handler->thrownErrors | $handler->loggedErrors);
set_error_handler(array($handler, 'handleError'), $handler->thrownErrors | $handler->loggedErrors);
$handler->isRoot = true;
}
@@ -140,12 +137,12 @@ class ErrorHandler
} else {
$handlerIsRegistered = true;
}
if (\is_array($prev = set_exception_handler([$handler, 'handleException'])) && $prev[0] instanceof self) {
if (\is_array($prev = set_exception_handler(array($handler, 'handleException'))) && $prev[0] instanceof self) {
restore_exception_handler();
if (!$handlerIsRegistered) {
$handler = $prev[0];
} elseif ($handler !== $prev[0] && $replace) {
set_exception_handler([$handler, 'handleException']);
set_exception_handler(array($handler, 'handleException'));
$p = $prev[0]->setExceptionHandler(null);
$handler->setExceptionHandler($p);
$prev[0]->setExceptionHandler($p);
@@ -178,12 +175,12 @@ class ErrorHandler
*/
public function setDefaultLogger(LoggerInterface $logger, $levels = E_ALL, $replace = false)
{
$loggers = [];
$loggers = array();
if (\is_array($levels)) {
foreach ($levels as $type => $logLevel) {
if (empty($this->loggers[$type][0]) || $replace || $this->loggers[$type][0] === $this->bootstrappingLogger) {
$loggers[$type] = [$logger, $logLevel];
$loggers[$type] = array($logger, $logLevel);
}
}
} else {
@@ -214,15 +211,15 @@ class ErrorHandler
{
$prevLogged = $this->loggedErrors;
$prev = $this->loggers;
$flush = [];
$flush = array();
foreach ($loggers as $type => $log) {
if (!isset($prev[$type])) {
throw new \InvalidArgumentException('Unknown error type: '.$type);
}
if (!\is_array($log)) {
$log = [$log];
} elseif (!\array_key_exists(0, $log)) {
$log = array($log);
} elseif (!array_key_exists(0, $log)) {
throw new \InvalidArgumentException('No logger provided');
}
if (null === $log[0]) {
@@ -358,9 +355,9 @@ class ErrorHandler
if ($handler === $this) {
restore_error_handler();
if ($this->isRoot) {
set_error_handler([$this, 'handleError'], $this->thrownErrors | $this->loggedErrors);
set_error_handler(array($this, 'handleError'), $this->thrownErrors | $this->loggedErrors);
} else {
set_error_handler([$this, 'handleError']);
set_error_handler(array($this, 'handleError'));
}
}
}
@@ -397,9 +394,9 @@ class ErrorHandler
$scope = $this->scopedErrors & $type;
if (4 < $numArgs = \func_num_args()) {
$context = $scope ? (func_get_arg(4) ?: []) : [];
$context = $scope ? (func_get_arg(4) ?: array()) : array();
} else {
$context = [];
$context = array();
}
if (isset($context['GLOBALS']) && $scope) {
@@ -408,30 +405,26 @@ class ErrorHandler
$context = $e;
}
if (false !== strpos($message, "class@anonymous\0")) {
$logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
} else {
$logMessage = $this->levels[$type].': '.$message;
}
$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, 5), $type, $file, $line, false) : [];
$errorAsException = new SilencedErrorContext($type, $file, $line, isset($lightTrace[1]) ? [$lightTrace[0]] : $lightTrace);
$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 = [];
$lightTrace = array();
$errorAsException = null;
}
if (100 < ++self::$silencedErrorCount) {
self::$silencedErrorCache = $lightTrace = [];
self::$silencedErrorCache = $lightTrace = array();
self::$silencedErrorCount = 1;
}
if ($errorAsException) {
@@ -443,13 +436,14 @@ class ErrorHandler
} 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 = $errorAsException->getTrace();
$lightTrace = $this->cleanTrace($backtrace, $type, $file, $line, $throw);
$this->traceReflector->setValue($errorAsException, $lightTrace);
} else {
$this->traceReflector->setValue($errorAsException, []);
$backtrace = [];
$this->traceReflector->setValue($errorAsException, array());
$backtrace = array();
}
}
@@ -492,21 +486,12 @@ class ErrorHandler
if ($this->isRecursive) {
$log = 0;
} else {
if (!\defined('HHVM_VERSION')) {
$currentErrorHandler = set_error_handler('var_dump');
restore_error_handler();
}
try {
$this->isRecursive = true;
$level = ($type & $level) ? $this->loggers[$type][1] : LogLevel::DEBUG;
$this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? ['exception' => $errorAsException] : []);
$this->loggers[$type][0]->log($level, $logMessage, $errorAsException ? array('exception' => $errorAsException) : array());
} finally {
$this->isRecursive = false;
if (!\defined('HHVM_VERSION')) {
set_error_handler($currentErrorHandler);
}
}
}
@@ -533,29 +518,26 @@ class ErrorHandler
$handlerException = null;
if (($this->loggedErrors & $type) || $exception instanceof FatalThrowableError) {
if (false !== strpos($message = $exception->getMessage(), "class@anonymous\0")) {
$message = (new FlattenException())->setMessage($message)->getMessage();
}
if ($exception instanceof FatalErrorException) {
if ($exception instanceof FatalThrowableError) {
$error = [
$error = array(
'type' => $type,
'message' => $message,
'message' => $message = $exception->getMessage(),
'file' => $exception->getFile(),
'line' => $exception->getLine(),
];
);
} else {
$message = 'Fatal '.$message;
$message = 'Fatal '.$exception->getMessage();
}
} elseif ($exception instanceof \ErrorException) {
$message = 'Uncaught '.$message;
$message = 'Uncaught '.$exception->getMessage();
} else {
$message = 'Uncaught Exception: '.$message;
$message = 'Uncaught Exception: '.$exception->getMessage();
}
}
if ($this->loggedErrors & $type) {
try {
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, ['exception' => $exception]);
$this->loggers[$type][0]->log($this->loggers[$type][1], $message, array('exception' => $exception));
} catch (\Throwable $handlerException) {
}
}
@@ -571,7 +553,7 @@ class ErrorHandler
$this->exceptionHandler = null;
try {
if (null !== $exceptionHandler) {
return $exceptionHandler($exception);
return \call_user_func($exceptionHandler, $exception);
}
$handlerException = $handlerException ?: $exception;
} catch (\Throwable $handlerException) {
@@ -597,7 +579,7 @@ class ErrorHandler
}
$handler = self::$reservedMemory = null;
$handlers = [];
$handlers = array();
$previousHandler = null;
$sameHandlerLimit = 10;
@@ -628,7 +610,7 @@ class ErrorHandler
$handler[0]->setExceptionHandler($h);
}
$handler = $handler[0];
$handlers = [];
$handlers = array();
if ($exit = null === $error) {
$error = error_get_last();
@@ -672,16 +654,13 @@ class ErrorHandler
*/
protected function getFatalErrorHandlers()
{
return [
return array(
new UndefinedFunctionFatalErrorHandler(),
new UndefinedMethodFatalErrorHandler(),
new ClassNotFoundFatalErrorHandler(),
];
);
}
/**
* Cleans the trace by removing function arguments and the frames added by the error handler and DebugClassLoader.
*/
private function cleanTrace($backtrace, $type, $file, $line, $throw)
{
$lightTrace = $backtrace;
@@ -692,13 +671,6 @@ class ErrorHandler
break;
}
}
if (class_exists(DebugClassLoader::class, false)) {
for ($i = \count($lightTrace) - 2; 0 < $i; --$i) {
if (DebugClassLoader::class === ($lightTrace[$i]['class'] ?? null)) {
array_splice($lightTrace, --$i, 2);
}
}
}
if (!($throw || $this->scopedErrors & $type)) {
for ($i = 0; isset($lightTrace[$i]); ++$i) {
unset($lightTrace[$i]['args'], $lightTrace[$i]['object']);

View File

@@ -61,7 +61,7 @@ class FatalErrorException extends \ErrorException
unset($frame);
$trace = array_reverse($trace);
} else {
$trace = [];
$trace = array();
}
$this->setTrace($trace);

View File

@@ -18,22 +18,21 @@ namespace Symfony\Component\Debug\Exception;
*/
class FatalThrowableError extends FatalErrorException
{
private $originalClassName;
public function __construct(\Throwable $e)
{
$this->originalClassName = \get_class($e);
if ($e instanceof \ParseError) {
$message = 'Parse error: '.$e->getMessage();
$severity = E_PARSE;
} elseif ($e instanceof \TypeError) {
$message = 'Type error: '.$e->getMessage();
$severity = E_RECOVERABLE_ERROR;
} else {
$message = $e->getMessage();
$severity = E_ERROR;
}
\ErrorException::__construct(
$e->getMessage(),
$message,
$e->getCode(),
$severity,
$e->getFile(),
@@ -43,9 +42,4 @@ class FatalThrowableError extends FatalErrorException
$this->setTrace($e->getTrace());
}
public function getOriginalClassName(): string
{
return $this->originalClassName;
}
}

View File

@@ -15,7 +15,7 @@ use Symfony\Component\HttpFoundation\Exception\RequestExceptionInterface;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
/**
* FlattenException wraps a PHP Error or Exception to be able to serialize it.
* FlattenException wraps a PHP Exception to be able to serialize it.
*
* Basically, this class removes all objects from the trace.
*
@@ -27,19 +27,13 @@ class FlattenException
private $code;
private $previous;
private $trace;
private $traceAsString;
private $class;
private $statusCode;
private $headers;
private $file;
private $line;
public static function create(\Exception $exception, $statusCode = null, array $headers = [])
{
return static::createFromThrowable($exception, $statusCode, $headers);
}
public static function createFromThrowable(\Throwable $exception, ?int $statusCode = null, array $headers = []): self
public static function create(\Exception $exception, $statusCode = null, array $headers = array())
{
$e = new static();
$e->setMessage($exception->getMessage());
@@ -58,15 +52,17 @@ class FlattenException
$e->setStatusCode($statusCode);
$e->setHeaders($headers);
$e->setTraceFromThrowable($exception);
$e->setClass($exception instanceof FatalThrowableError ? $exception->getOriginalClassName() : \get_class($exception));
$e->setTraceFromException($exception);
$e->setClass(\get_class($exception));
$e->setFile($exception->getFile());
$e->setLine($exception->getLine());
$previous = $exception->getPrevious();
if ($previous instanceof \Throwable) {
$e->setPrevious(static::createFromThrowable($previous));
if ($previous instanceof \Exception) {
$e->setPrevious(static::create($previous));
} elseif ($previous instanceof \Throwable) {
$e->setPrevious(static::create(new FatalThrowableError($previous)));
}
return $e;
@@ -74,13 +70,13 @@ class FlattenException
public function toArray()
{
$exceptions = [];
foreach (array_merge([$this], $this->getAllPrevious()) as $exception) {
$exceptions[] = [
$exceptions = array();
foreach (array_merge(array($this), $this->getAllPrevious()) as $exception) {
$exceptions[] = array(
'message' => $exception->getMessage(),
'class' => $exception->getClass(),
'trace' => $exception->getTrace(),
];
);
}
return $exceptions;
@@ -91,14 +87,9 @@ class FlattenException
return $this->statusCode;
}
/**
* @return $this
*/
public function setStatusCode($code)
{
$this->statusCode = $code;
return $this;
}
public function getHeaders()
@@ -106,14 +97,9 @@ class FlattenException
return $this->headers;
}
/**
* @return $this
*/
public function setHeaders(array $headers)
{
$this->headers = $headers;
return $this;
}
public function getClass()
@@ -121,14 +107,9 @@ class FlattenException
return $this->class;
}
/**
* @return $this
*/
public function setClass($class)
{
$this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
return $this;
$this->class = $class;
}
public function getFile()
@@ -136,14 +117,9 @@ class FlattenException
return $this->file;
}
/**
* @return $this
*/
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getLine()
@@ -151,14 +127,9 @@ class FlattenException
return $this->line;
}
/**
* @return $this
*/
public function setLine($line)
{
$this->line = $line;
return $this;
}
public function getMessage()
@@ -166,20 +137,9 @@ class FlattenException
return $this->message;
}
/**
* @return $this
*/
public function setMessage($message)
{
if (false !== strpos($message, "class@anonymous\0")) {
$message = preg_replace_callback('/class@anonymous\x00.*?\.php0x?[0-9a-fA-F]++/', function ($m) {
return \class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0];
}, $message);
}
$this->message = $message;
return $this;
}
public function getCode()
@@ -187,14 +147,9 @@ class FlattenException
return $this->code;
}
/**
* @return $this
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
public function getPrevious()
@@ -202,19 +157,14 @@ class FlattenException
return $this->previous;
}
/**
* @return $this
*/
public function setPrevious(self $previous)
{
$this->previous = $previous;
return $this;
}
public function getAllPrevious()
{
$exceptions = [];
$exceptions = array();
$e = $this;
while ($e = $e->getPrevious()) {
$exceptions[] = $e;
@@ -228,30 +178,15 @@ class FlattenException
return $this->trace;
}
/**
* @deprecated since 4.1, use {@see setTraceFromThrowable()} instead.
*/
public function setTraceFromException(\Exception $exception)
{
@trigger_error(sprintf('The "%s()" method is deprecated since Symfony 4.1, use "setTraceFromThrowable()" instead.', __METHOD__), E_USER_DEPRECATED);
$this->setTraceFromThrowable($exception);
$this->setTrace($exception->getTrace(), $exception->getFile(), $exception->getLine());
}
public function setTraceFromThrowable(\Throwable $throwable)
{
$this->traceAsString = $throwable->getTraceAsString();
return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
}
/**
* @return $this
*/
public function setTrace($trace, $file, $line)
{
$this->trace = [];
$this->trace[] = [
$this->trace = array();
$this->trace[] = array(
'namespace' => '',
'short_class' => '',
'class' => '',
@@ -259,8 +194,8 @@ class FlattenException
'function' => '',
'file' => $file,
'line' => $line,
'args' => [],
];
'args' => array(),
);
foreach ($trace as $entry) {
$class = '';
$namespace = '';
@@ -270,7 +205,7 @@ class FlattenException
$namespace = implode('\\', $parts);
}
$this->trace[] = [
$this->trace[] = array(
'namespace' => $namespace,
'short_class' => $class,
'class' => isset($entry['class']) ? $entry['class'] : '',
@@ -278,43 +213,41 @@ class FlattenException
'function' => isset($entry['function']) ? $entry['function'] : null,
'file' => isset($entry['file']) ? $entry['file'] : null,
'line' => isset($entry['line']) ? $entry['line'] : null,
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [],
];
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : array(),
);
}
return $this;
}
private function flattenArgs($args, $level = 0, &$count = 0)
{
$result = [];
$result = array();
foreach ($args as $key => $value) {
if (++$count > 1e4) {
return ['array', '*SKIPPED over 10000 entries*'];
return array('array', '*SKIPPED over 10000 entries*');
}
if ($value instanceof \__PHP_Incomplete_Class) {
// is_object() returns false on PHP<=7.1
$result[$key] = ['incomplete-object', $this->getClassNameFromIncomplete($value)];
$result[$key] = array('incomplete-object', $this->getClassNameFromIncomplete($value));
} elseif (\is_object($value)) {
$result[$key] = ['object', \get_class($value)];
$result[$key] = array('object', \get_class($value));
} elseif (\is_array($value)) {
if ($level > 10) {
$result[$key] = ['array', '*DEEP NESTED ARRAY*'];
$result[$key] = array('array', '*DEEP NESTED ARRAY*');
} else {
$result[$key] = ['array', $this->flattenArgs($value, $level + 1, $count)];
$result[$key] = array('array', $this->flattenArgs($value, $level + 1, $count));
}
} elseif (null === $value) {
$result[$key] = ['null', null];
$result[$key] = array('null', null);
} elseif (\is_bool($value)) {
$result[$key] = ['boolean', $value];
$result[$key] = array('boolean', $value);
} elseif (\is_int($value)) {
$result[$key] = ['integer', $value];
$result[$key] = array('integer', $value);
} elseif (\is_float($value)) {
$result[$key] = ['float', $value];
$result[$key] = array('float', $value);
} elseif (\is_resource($value)) {
$result[$key] = ['resource', get_resource_type($value)];
$result[$key] = array('resource', get_resource_type($value));
} else {
$result[$key] = ['string', (string) $value];
$result[$key] = array('string', (string) $value);
}
}
@@ -327,33 +260,4 @@ class FlattenException
return $array['__PHP_Incomplete_Class_Name'];
}
public function getTraceAsString()
{
return $this->traceAsString;
}
public function getAsString()
{
$message = '';
$next = false;
foreach (array_reverse(array_merge([$this], $this->getAllPrevious())) as $exception) {
if ($next) {
$message .= 'Next ';
} else {
$next = true;
}
$message .= $exception->getClass();
if ('' != $exception->getMessage()) {
$message .= ': '.$exception->getMessage();
}
$message .= ' in '.$exception->getFile().':'.$exception->getLine().
"\nStack trace:\n".$exception->getTraceAsString()."\n\n";
}
return rtrim($message);
}
}

View File

@@ -25,7 +25,7 @@ class SilencedErrorContext implements \JsonSerializable
private $line;
private $trace;
public function __construct(int $severity, string $file, int $line, array $trace = [], int $count = 1)
public function __construct(int $severity, string $file, int $line, array $trace = array(), int $count = 1)
{
$this->severity = $severity;
$this->file = $file;
@@ -56,12 +56,12 @@ class SilencedErrorContext implements \JsonSerializable
public function JsonSerialize()
{
return [
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

@@ -40,7 +40,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
return;
}
foreach (['class', 'interface', 'trait'] as $typeName) {
foreach (array('class', 'interface', 'trait') as $typeName) {
$prefix = ucfirst($typeName).' \'';
$prefixLen = \strlen($prefix);
if (0 !== strpos($error['message'], $prefix)) {
@@ -86,11 +86,11 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
private function getClassCandidates(string $class): array
{
if (!\is_array($functions = spl_autoload_functions())) {
return [];
return array();
}
// find Symfony and Composer autoloaders
$classes = [];
$classes = array();
foreach ($functions as $function) {
if (!\is_array($function)) {
@@ -127,10 +127,10 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
private function findClassInPath(string $path, string $class, string $prefix): array
{
if (!$path = realpath($path.'/'.strtr($prefix, '\\_', '//')) ?: realpath($path.'/'.\dirname(strtr($prefix, '\\_', '//'))) ?: realpath($path)) {
return [];
return array();
}
$classes = [];
$classes = array();
$filename = $class.'.php';
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
if ($filename == $file->getFileName() && $class = $this->convertFileToClass($path, $file->getPathName(), $prefix)) {
@@ -143,9 +143,9 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
private function convertFileToClass(string $path, string $file, string $prefix): ?string
{
$candidates = [
$candidates = array(
// namespaced class
$namespacedClass = str_replace([$path.\DIRECTORY_SEPARATOR, '.php', '/'], ['', '', '\\'], $file),
$namespacedClass = str_replace(array($path.\DIRECTORY_SEPARATOR, '.php', '/'), array('', '', '\\'), $file),
// namespaced class (with target dir)
$prefix.$namespacedClass,
// namespaced class (with target dir and separator)
@@ -156,7 +156,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
str_replace('\\', '_', $prefix.$namespacedClass),
// PEAR class (with target dir and separator)
str_replace('\\', '_', $prefix.'\\'.$namespacedClass),
];
);
if ($prefix) {
$candidates = array_filter($candidates, function ($candidate) use ($prefix) { return 0 === strpos($candidate, $prefix); });
@@ -171,11 +171,7 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
}
}
try {
require_once $file;
} catch (\Throwable $e) {
return null;
}
require_once $file;
foreach ($candidates as $candidate) {
if ($this->classExists($candidate)) {

View File

@@ -53,7 +53,7 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
$message = sprintf('Attempted to call function "%s" from the global namespace.', $functionName);
}
$candidates = [];
$candidates = array();
foreach (get_defined_functions() as $type => $definedFunctionNames) {
foreach ($definedFunctionNames as $definedFunctionName) {
if (false !== $namespaceSeparatorIndex = strrpos($definedFunctionName, '\\')) {

View File

@@ -41,7 +41,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
return new UndefinedMethodException($message, $exception);
}
$candidates = [];
$candidates = array();
foreach ($methods as $definedMethodName) {
$lev = levenshtein($methodName, $definedMethodName);
if ($lev <= \strlen($methodName) / 3 || false !== strpos($definedMethodName, $methodName)) {

View File

@@ -1,4 +1,4 @@
Copyright (c) 2004-2019 Fabien Potencier
Copyright (c) 2004-2018 Fabien Potencier
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -27,14 +27,14 @@ class DebugClassLoaderTest extends TestCase
{
$this->errorReporting = error_reporting(E_ALL);
$this->loader = new ClassLoader();
spl_autoload_register([$this->loader, 'loadClass'], true, true);
spl_autoload_register(array($this->loader, 'loadClass'), true, true);
DebugClassLoader::enable();
}
protected function tearDown()
{
DebugClassLoader::disable();
spl_autoload_unregister([$this->loader, 'loadClass']);
spl_autoload_unregister(array($this->loader, 'loadClass'));
error_reporting($this->errorReporting);
}
@@ -44,7 +44,7 @@ class DebugClassLoaderTest extends TestCase
$functions = spl_autoload_functions();
foreach ($functions as $function) {
if (\is_array($function) && $function[0] instanceof DebugClassLoader) {
if (is_array($function) && $function[0] instanceof DebugClassLoader) {
$reflClass = new \ReflectionClass($function[0]);
$reflProp = $reflClass->getProperty('classLoader');
$reflProp->setAccessible(true);
@@ -136,20 +136,20 @@ class DebugClassLoaderTest extends TestCase
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$xError = [
$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.',
];
);
$this->assertSame($xError, $lastError);
}
public function provideDeprecatedSuper()
{
return [
['DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'],
['DeprecatedParentClass', 'DeprecatedClass', 'extends'],
];
return array(
array('DeprecatedInterfaceClass', 'DeprecatedInterface', 'implements'),
array('DeprecatedParentClass', 'DeprecatedClass', 'extends'),
);
}
public function testInterfaceExtendsDeprecatedInterface()
@@ -166,10 +166,10 @@ class DebugClassLoaderTest extends TestCase
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$xError = [
$xError = array(
'type' => E_USER_NOTICE,
'message' => '',
];
);
$this->assertSame($xError, $lastError);
}
@@ -188,60 +188,56 @@ class DebugClassLoaderTest extends TestCase
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$xError = [
$xError = array(
'type' => E_USER_NOTICE,
'message' => '',
];
);
$this->assertSame($xError, $lastError);
}
public function testExtendedFinalClass()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
set_error_handler(function () { return false; });
$e = error_reporting(0);
trigger_error('', E_USER_NOTICE);
require __DIR__.'/Fixtures/FinalClasses.php';
$i = 1;
while (class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) {
spl_autoload_call($finalClass);
class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true);
}
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsFinalClass', true);
error_reporting($e);
restore_error_handler();
$this->assertSame([
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass1" 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\ExtendsFinalClass1".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass2" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass2".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass3" class is considered final comment with @@@ and ***. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass3".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass4" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass4".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass5" class is considered final multiline comment. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass5".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass6" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass6".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass7" class is considered final another multiline comment... It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass7".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalClass8" class is considered final. It may change without further notice as of its next major version. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsFinalClass8".',
], $deprecations);
$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. 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()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
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();
$xError = [
'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".',
];
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$this->assertSame($xError, $deprecations);
$xError = array(
'type' => E_USER_DEPRECATED,
'message' => 'The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. 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()
@@ -258,12 +254,12 @@ class DebugClassLoaderTest extends TestCase
$lastError = error_get_last();
unset($lastError['file'], $lastError['line']);
$this->assertSame(['type' => E_USER_NOTICE, 'message' => ''], $lastError);
$this->assertSame(array('type' => E_USER_NOTICE, 'message' => ''), $lastError);
}
public function testInternalsUse()
{
$deprecations = [];
$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
@@ -272,94 +268,12 @@ class DebugClassLoaderTest extends TestCase
error_reporting($e);
restore_error_handler();
$this->assertSame($deprecations, [
'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".',
$this->assertSame($deprecations, array(
'The "Symfony\Component\Debug\Tests\Fixtures\InternalClass" class 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\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\InternalClass::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
]);
}
public function testExtendedMethodDefinesNewParameters()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists(__NAMESPACE__.'\\Fixtures\SubClassWithAnnotatedParameters', true);
error_reporting($e);
restore_error_handler();
$this->assertSame([
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::quzMethod()" method will require a new "Quz $quz" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::whereAmI()" method will require a new "bool $matrix" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "$noType" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable(\Throwable|null $reason, mixed $value) $callback" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "string $param" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "callable ($a, $b) $anotherOne" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::iAmHere()" method will require a new "Type$WithDollarIsStillAType $ccc" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\InterfaceWithAnnotatedParameters", not defining it is deprecated.',
'The "Symfony\Component\Debug\Tests\Fixtures\SubClassWithAnnotatedParameters::isSymfony()" method will require a new "true $yes" argument in the next major version of its parent class "Symfony\Component\Debug\Tests\Fixtures\ClassWithAnnotatedParameters", not defining it is deprecated.',
], $deprecations);
}
public function testUseTraitWithInternalMethod()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\UseTraitWithInternalMethod', true);
error_reporting($e);
restore_error_handler();
$this->assertSame([], $deprecations);
}
public function testVirtualUse()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsVirtual', true);
error_reporting($e);
restore_error_handler();
$this->assertSame([
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::sameLineInterfaceMethodNoBraces()".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethod()": Some description!',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::newLineInterfaceMethodNoBraces()": Description.',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethod()".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::invalidInterfaceMethodNoBraces()".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethod($arg, ...$args)".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::complexInterfaceMethodTyped($arg, int ...$args)": Description ...',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodNoBraces()".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTyped(int $arg)": Description.',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtualParent" should implement method "static Symfony\Component\Debug\Tests\Fixtures\VirtualInterface::staticMethodTypedNoBraces()".',
'Class "Test\Symfony\Component\Debug\Tests\ExtendsVirtual" should implement method "Symfony\Component\Debug\Tests\Fixtures\VirtualSubInterface::subInterfaceMethod()".',
], $deprecations);
}
public function testVirtualUseWithMagicCall()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsVirtualMagicCall', true);
error_reporting($e);
restore_error_handler();
$this->assertSame([], $deprecations);
}
public function testEvaluatedCode()
{
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\DefinitionInEvaluatedCode', true));
'The "Symfony\Component\Debug\Tests\Fixtures\InternalTrait2::internalMethod()" method is considered internal. It may change without further notice. You should not extend it from "Test\Symfony\Component\Debug\Tests\ExtendsInternals".',
));
}
}
@@ -371,12 +285,12 @@ class ClassLoader
public function getClassMap()
{
return [__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php'];
return array(__NAMESPACE__.'\Fixtures\NotPSR0bis' => __DIR__.'/Fixtures/notPsr0Bis.php');
}
public function findFile($class)
{
$fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR;
$fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR;
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
eval('-- parse error --');
@@ -385,7 +299,7 @@ class ClassLoader
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
eval('namespace '.__NAMESPACE__.'; class TestingCaseMisMatch {}');
} elseif (__NAMESPACE__.'\Fixtures\Psr4CaseMismatch' === $class) {
return $fixtureDir.'psr4'.\DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
return $fixtureDir.'psr4'.DIRECTORY_SEPARATOR.'Psr4CaseMismatch.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0' === $class) {
return $fixtureDir.'reallyNotPsr0.php';
} elseif (__NAMESPACE__.'\Fixtures\NotPSR0bis' === $class) {
@@ -400,9 +314,8 @@ class ClassLoader
eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
} elseif (0 === strpos($class, 'Test\\'.__NAMESPACE__.'\ExtendsFinalClass')) {
$classShortName = substr($class, strrpos($class, '\\') + 1);
eval('namespace Test\\'.__NAMESPACE__.'; class '.$classShortName.' extends \\'.__NAMESPACE__.'\Fixtures\\'.substr($classShortName, 7).' {}');
} 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() { }
@@ -415,34 +328,6 @@ class ClassLoader
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
} elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtual' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtual extends ExtendsVirtualParent implements \\'.__NAMESPACE__.'\Fixtures\VirtualSubInterface {
public function ownClassMethod() { }
public function classMethod() { }
public function sameLineInterfaceMethodNoBraces() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualParent' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualParent extends ExtendsVirtualAbstract {
public function ownParentMethod() { }
public function traitMethod() { }
public function sameLineInterfaceMethod() { }
public function staticMethodNoBraces() { } // should be static
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualAbstract' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstract extends ExtendsVirtualAbstractBase {
public static function staticMethod() { }
public function ownAbstractMethod() { }
public function interfaceMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualAbstractBase' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstractBase extends \\'.__NAMESPACE__.'\Fixtures\VirtualClass implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
public function ownAbstractBaseMethod() { }
}');
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsVirtualMagicCall' === $class) {
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualMagicCall extends \\'.__NAMESPACE__.'\Fixtures\VirtualClassMagicCall implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
}');
}
}
}

View File

@@ -13,12 +13,9 @@ namespace Symfony\Component\Debug\Tests;
use PHPUnit\Framework\TestCase;
use Psr\Log\LogLevel;
use Psr\Log\NullLogger;
use Symfony\Component\Debug\BufferingLogger;
use Symfony\Component\Debug\ErrorHandler;
use Symfony\Component\Debug\Exception\SilencedErrorContext;
use Symfony\Component\Debug\Tests\Fixtures\ErrorHandlerThatUsesThePreviousOne;
use Symfony\Component\Debug\Tests\Fixtures\LoggerThatSetAnErrorHandler;
/**
* ErrorHandlerTest.
@@ -41,13 +38,13 @@ class ErrorHandlerTest extends TestCase
$this->assertSame($handler, ErrorHandler::register($newHandler, false));
$h = set_error_handler('var_dump');
restore_error_handler();
$this->assertSame([$handler, 'handleError'], $h);
$this->assertSame(array($handler, 'handleError'), $h);
try {
$this->assertSame($newHandler, ErrorHandler::register($newHandler, true));
$h = set_error_handler('var_dump');
restore_error_handler();
$this->assertSame([$newHandler, 'handleError'], $h);
$this->assertSame(array($newHandler, 'handleError'), $h);
} catch (\Exception $e) {
}
@@ -77,12 +74,12 @@ class ErrorHandlerTest extends TestCase
try {
@trigger_error('Hello', E_USER_WARNING);
$expected = [
$expected = array(
'type' => E_USER_WARNING,
'message' => 'Hello',
'file' => __FILE__,
'line' => __LINE__ - 5,
];
);
$this->assertSame($expected, error_get_last());
} catch (\Exception $e) {
restore_error_handler();
@@ -148,26 +145,26 @@ class ErrorHandlerTest extends TestCase
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$handler->setDefaultLogger($logger, E_NOTICE);
$handler->setDefaultLogger($logger, [E_USER_NOTICE => LogLevel::CRITICAL]);
$handler->setDefaultLogger($logger, array(E_USER_NOTICE => LogLevel::CRITICAL));
$loggers = [
E_DEPRECATED => [null, LogLevel::INFO],
E_USER_DEPRECATED => [null, LogLevel::INFO],
E_NOTICE => [$logger, LogLevel::WARNING],
E_USER_NOTICE => [$logger, LogLevel::CRITICAL],
E_STRICT => [null, LogLevel::WARNING],
E_WARNING => [null, LogLevel::WARNING],
E_USER_WARNING => [null, LogLevel::WARNING],
E_COMPILE_WARNING => [null, LogLevel::WARNING],
E_CORE_WARNING => [null, LogLevel::WARNING],
E_USER_ERROR => [null, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [null, LogLevel::CRITICAL],
E_COMPILE_ERROR => [null, LogLevel::CRITICAL],
E_PARSE => [null, LogLevel::CRITICAL],
E_ERROR => [null, LogLevel::CRITICAL],
E_CORE_ERROR => [null, LogLevel::CRITICAL],
];
$this->assertSame($loggers, $handler->setLoggers([]));
$loggers = array(
E_DEPRECATED => array(null, LogLevel::INFO),
E_USER_DEPRECATED => array(null, LogLevel::INFO),
E_NOTICE => array($logger, LogLevel::WARNING),
E_USER_NOTICE => array($logger, LogLevel::CRITICAL),
E_STRICT => array(null, LogLevel::WARNING),
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::CRITICAL),
E_RECOVERABLE_ERROR => array(null, LogLevel::CRITICAL),
E_COMPILE_ERROR => array(null, LogLevel::CRITICAL),
E_PARSE => array(null, LogLevel::CRITICAL),
E_ERROR => array(null, LogLevel::CRITICAL),
E_CORE_ERROR => array(null, LogLevel::CRITICAL),
);
$this->assertSame($loggers, $handler->setLoggers(array()));
} finally {
restore_error_handler();
restore_exception_handler();
@@ -179,14 +176,14 @@ class ErrorHandlerTest extends TestCase
try {
$handler = ErrorHandler::register();
$handler->throwAt(0, true);
$this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, []));
$this->assertFalse($handler->handleError(0, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
$this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, []));
$this->assertFalse($handler->handleError(4, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
@@ -194,7 +191,7 @@ class ErrorHandlerTest extends TestCase
$handler = ErrorHandler::register();
$handler->throwAt(3, true);
try {
$handler->handleError(4, 'foo', 'foo.php', 12, []);
$handler->handleError(4, 'foo', 'foo.php', 12, array());
} catch (\ErrorException $e) {
$this->assertSame('Parse Error: foo', $e->getMessage());
$this->assertSame(4, $e->getSeverity());
@@ -207,14 +204,14 @@ class ErrorHandlerTest extends TestCase
$handler = ErrorHandler::register();
$handler->throwAt(E_USER_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
$this->assertFalse($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
$handler = ErrorHandler::register();
$handler->throwAt(E_DEPRECATED, true);
$this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, []));
$this->assertFalse($handler->handleError(E_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
@@ -239,7 +236,7 @@ class ErrorHandlerTest extends TestCase
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger, E_USER_DEPRECATED);
$this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, []));
$this->assertTrue($handler->handleError(E_USER_DEPRECATED, 'foo', 'foo.php', 12, array()));
restore_error_handler();
restore_exception_handler();
@@ -323,9 +320,7 @@ class ErrorHandlerTest extends TestCase
$handler = new ErrorHandler();
$handler->setDefaultLogger($logger);
@$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, []);
restore_error_handler();
@$handler->handleError(E_USER_DEPRECATED, 'Foo deprecation', __FILE__, __LINE__, array());
}
public function testHandleException()
@@ -374,27 +369,27 @@ class ErrorHandlerTest extends TestCase
$bootLogger = new BufferingLogger();
$handler = new ErrorHandler($bootLogger);
$loggers = [
E_DEPRECATED => [$bootLogger, LogLevel::INFO],
E_USER_DEPRECATED => [$bootLogger, LogLevel::INFO],
E_NOTICE => [$bootLogger, LogLevel::WARNING],
E_USER_NOTICE => [$bootLogger, LogLevel::WARNING],
E_STRICT => [$bootLogger, LogLevel::WARNING],
E_WARNING => [$bootLogger, LogLevel::WARNING],
E_USER_WARNING => [$bootLogger, LogLevel::WARNING],
E_COMPILE_WARNING => [$bootLogger, LogLevel::WARNING],
E_CORE_WARNING => [$bootLogger, LogLevel::WARNING],
E_USER_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_RECOVERABLE_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_COMPILE_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_PARSE => [$bootLogger, LogLevel::CRITICAL],
E_ERROR => [$bootLogger, LogLevel::CRITICAL],
E_CORE_ERROR => [$bootLogger, LogLevel::CRITICAL],
];
$loggers = array(
E_DEPRECATED => array($bootLogger, LogLevel::INFO),
E_USER_DEPRECATED => array($bootLogger, LogLevel::INFO),
E_NOTICE => array($bootLogger, LogLevel::WARNING),
E_USER_NOTICE => array($bootLogger, LogLevel::WARNING),
E_STRICT => array($bootLogger, LogLevel::WARNING),
E_WARNING => array($bootLogger, LogLevel::WARNING),
E_USER_WARNING => array($bootLogger, LogLevel::WARNING),
E_COMPILE_WARNING => array($bootLogger, LogLevel::WARNING),
E_CORE_WARNING => array($bootLogger, LogLevel::WARNING),
E_USER_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_RECOVERABLE_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_COMPILE_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_PARSE => array($bootLogger, LogLevel::CRITICAL),
E_ERROR => array($bootLogger, LogLevel::CRITICAL),
E_CORE_ERROR => array($bootLogger, LogLevel::CRITICAL),
);
$this->assertSame($loggers, $handler->setLoggers([]));
$this->assertSame($loggers, $handler->setLoggers(array()));
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, []);
$handler->handleError(E_DEPRECATED, 'Foo message', __FILE__, 123, array());
$logs = $bootLogger->cleanLogs();
@@ -410,14 +405,14 @@ class ErrorHandlerTest extends TestCase
$this->assertSame(123, $exception->getLine());
$this->assertSame(E_DEPRECATED, $exception->getSeverity());
$bootLogger->log(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
$bootLogger->log(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$mockLogger->expects($this->once())
->method('log')
->with(LogLevel::WARNING, 'Foo message', ['exception' => $exception]);
->with(LogLevel::WARNING, 'Foo message', array('exception' => $exception));
$handler->setLoggers([E_DEPRECATED => [$mockLogger, LogLevel::WARNING]]);
$handler->setLoggers(array(E_DEPRECATED => array($mockLogger, LogLevel::WARNING)));
}
public function testSettingLoggerWhenExceptionIsBuffered()
@@ -430,7 +425,7 @@ class ErrorHandlerTest extends TestCase
$mockLogger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
$mockLogger->expects($this->once())
->method('log')
->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', ['exception' => $exception]);
->with(LogLevel::CRITICAL, 'Uncaught Exception: Foo message', array('exception' => $exception));
$handler->setExceptionHandler(function () use ($handler, $mockLogger) {
$handler->setDefaultLogger($mockLogger);
@@ -444,12 +439,12 @@ class ErrorHandlerTest extends TestCase
try {
$handler = ErrorHandler::register();
$error = [
$error = array(
'type' => E_PARSE,
'message' => 'foo',
'file' => 'bar',
'line' => 123,
];
);
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
@@ -481,7 +476,7 @@ class ErrorHandlerTest extends TestCase
public function testHandleErrorException()
{
$exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found");
$exception = new \Error("Class 'Foo' not found");
$handler = new ErrorHandler();
$handler->setExceptionHandler(function () use (&$args) {
@@ -491,7 +486,7 @@ class ErrorHandlerTest extends TestCase
$handler->handleException($exception);
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
$this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
$this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
}
/**
@@ -506,58 +501,4 @@ class ErrorHandlerTest extends TestCase
$handler->handleException(new \Exception());
}
/**
* @dataProvider errorHandlerWhenLoggingProvider
*/
public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined)
{
try {
if ($previousHandlerWasDefined) {
set_error_handler('count');
}
$logger = $loggerSetsAnotherHandler ? new LoggerThatSetAnErrorHandler() : new NullLogger();
$handler = ErrorHandler::register();
$handler->setDefaultLogger($logger);
if ($nextHandlerIsDefined) {
$handler = ErrorHandlerThatUsesThePreviousOne::register();
}
@trigger_error('foo', E_USER_DEPRECATED);
@trigger_error('bar', E_USER_DEPRECATED);
$this->assertSame([$handler, 'handleError'], set_error_handler('var_dump'));
if ($logger instanceof LoggerThatSetAnErrorHandler) {
$this->assertCount(2, $logger->cleanLogs());
}
restore_error_handler();
if ($previousHandlerWasDefined) {
restore_error_handler();
}
if ($nextHandlerIsDefined) {
restore_error_handler();
}
} finally {
restore_error_handler();
restore_exception_handler();
}
}
public function errorHandlerWhenLoggingProvider()
{
foreach ([false, true] as $previousHandlerWasDefined) {
foreach ([false, true] as $loggerSetsAnotherHandler) {
foreach ([false, true] as $nextHandlerIsDefined) {
yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined];
}
}
}
}
}

View File

@@ -12,7 +12,6 @@
namespace Symfony\Component\Debug\Tests\Exception;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Debug\Exception\FatalThrowableError;
use Symfony\Component\Debug\Exception\FlattenException;
use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
@@ -40,12 +39,6 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create(new \RuntimeException());
$this->assertEquals('500', $flattened->getStatusCode());
$flattened = FlattenException::createFromThrowable(new \DivisionByZeroError(), 403);
$this->assertEquals('403', $flattened->getStatusCode());
$flattened = FlattenException::createFromThrowable(new \DivisionByZeroError());
$this->assertEquals('500', $flattened->getStatusCode());
$flattened = FlattenException::create(new NotFoundHttpException());
$this->assertEquals('404', $flattened->getStatusCode());
@@ -61,7 +54,7 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create(new ConflictHttpException());
$this->assertEquals('409', $flattened->getStatusCode());
$flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST']));
$flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST')));
$this->assertEquals('405', $flattened->getStatusCode());
$flattened = FlattenException::create(new AccessDeniedHttpException());
@@ -96,32 +89,32 @@ class FlattenExceptionTest extends TestCase
public function testHeadersForHttpException()
{
$flattened = FlattenException::create(new MethodNotAllowedHttpException(['POST']));
$this->assertEquals(['Allow' => 'POST'], $flattened->getHeaders());
$flattened = FlattenException::create(new MethodNotAllowedHttpException(array('POST')));
$this->assertEquals(array('Allow' => 'POST'), $flattened->getHeaders());
$flattened = FlattenException::create(new UnauthorizedHttpException('Basic realm="My Realm"'));
$this->assertEquals(['WWW-Authenticate' => 'Basic realm="My Realm"'], $flattened->getHeaders());
$this->assertEquals(array('WWW-Authenticate' => 'Basic realm="My Realm"'), $flattened->getHeaders());
$flattened = FlattenException::create(new ServiceUnavailableHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
$this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders());
$this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders());
$flattened = FlattenException::create(new ServiceUnavailableHttpException(120));
$this->assertEquals(['Retry-After' => 120], $flattened->getHeaders());
$this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders());
$flattened = FlattenException::create(new TooManyRequestsHttpException('Fri, 31 Dec 1999 23:59:59 GMT'));
$this->assertEquals(['Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'], $flattened->getHeaders());
$this->assertEquals(array('Retry-After' => 'Fri, 31 Dec 1999 23:59:59 GMT'), $flattened->getHeaders());
$flattened = FlattenException::create(new TooManyRequestsHttpException(120));
$this->assertEquals(['Retry-After' => 120], $flattened->getHeaders());
$this->assertEquals(array('Retry-After' => 120), $flattened->getHeaders());
}
/**
* @dataProvider flattenDataProvider
*/
public function testFlattenHttpException(\Throwable $exception)
public function testFlattenHttpException(\Exception $exception)
{
$flattened = FlattenException::createFromThrowable($exception);
$flattened2 = FlattenException::createFromThrowable($exception);
$flattened = FlattenException::create($exception);
$flattened2 = FlattenException::create($exception);
$flattened->setPrevious($flattened2);
@@ -130,39 +123,19 @@ class FlattenExceptionTest extends TestCase
$this->assertInstanceOf($flattened->getClass(), $exception, 'The class is set to the class of the original exception');
}
public function testWrappedThrowable()
{
$exception = new FatalThrowableError(new \DivisionByZeroError('Ouch', 42));
$flattened = FlattenException::create($exception);
$this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
$this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.');
$this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error');
}
public function testThrowable()
{
$error = new \DivisionByZeroError('Ouch', 42);
$flattened = FlattenException::createFromThrowable($error);
$this->assertSame('Ouch', $flattened->getMessage(), 'The message is copied from the original error.');
$this->assertSame(42, $flattened->getCode(), 'The code is copied from the original error.');
$this->assertSame('DivisionByZeroError', $flattened->getClass(), 'The class is set to the class of the original error');
}
/**
* @dataProvider flattenDataProvider
*/
public function testPrevious(\Throwable $exception)
public function testPrevious(\Exception $exception)
{
$flattened = FlattenException::createFromThrowable($exception);
$flattened2 = FlattenException::createFromThrowable($exception);
$flattened = FlattenException::create($exception);
$flattened2 = FlattenException::create($exception);
$flattened->setPrevious($flattened2);
$this->assertSame($flattened2, $flattened->getPrevious());
$this->assertSame([$flattened2], $flattened->getAllPrevious());
$this->assertSame(array($flattened2), $flattened->getAllPrevious());
}
public function testPreviousError()
@@ -171,68 +144,54 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create($exception)->getPrevious();
$this->assertEquals($flattened->getMessage(), 'Oh noes!', 'The message is copied from the original exception.');
$this->assertEquals($flattened->getMessage(), 'Parse error: Oh noes!', 'The message is copied from the original exception.');
$this->assertEquals($flattened->getCode(), 42, 'The code is copied from the original exception.');
$this->assertEquals($flattened->getClass(), 'ParseError', 'The class is set to the class of the original exception');
$this->assertEquals($flattened->getClass(), 'Symfony\Component\Debug\Exception\FatalThrowableError', 'The class is set to the class of the original exception');
}
/**
* @dataProvider flattenDataProvider
*/
public function testLine(\Throwable $exception)
public function testLine(\Exception $exception)
{
$flattened = FlattenException::createFromThrowable($exception);
$flattened = FlattenException::create($exception);
$this->assertSame($exception->getLine(), $flattened->getLine());
}
/**
* @dataProvider flattenDataProvider
*/
public function testFile(\Throwable $exception)
public function testFile(\Exception $exception)
{
$flattened = FlattenException::createFromThrowable($exception);
$flattened = FlattenException::create($exception);
$this->assertSame($exception->getFile(), $flattened->getFile());
}
/**
* @dataProvider flattenDataProvider
*/
public function testToArray(\Throwable $exception, string $expectedClass)
public function testToArray(\Exception $exception)
{
$flattened = FlattenException::createFromThrowable($exception);
$flattened->setTrace([], 'foo.php', 123);
$flattened = FlattenException::create($exception);
$flattened->setTrace(array(), 'foo.php', 123);
$this->assertEquals([
[
$this->assertEquals(array(
array(
'message' => 'test',
'class' => $expectedClass,
'trace' => [[
'class' => 'Exception',
'trace' => array(array(
'namespace' => '', 'short_class' => '', 'class' => '', 'type' => '', 'function' => '', 'file' => 'foo.php', 'line' => 123,
'args' => [],
]],
],
], $flattened->toArray());
}
public function testCreate()
{
$exception = new NotFoundHttpException(
'test',
new \RuntimeException('previous', 123)
);
$this->assertSame(
FlattenException::createFromThrowable($exception)->toArray(),
FlattenException::create($exception)->toArray()
);
'args' => array(),
)),
),
), $flattened->toArray());
}
public function flattenDataProvider()
{
return [
[new \Exception('test', 123), 'Exception'],
[new \Error('test', 123), 'Error'],
];
return array(
array(new \Exception('test', 123)),
);
}
public function testArguments()
@@ -242,15 +201,15 @@ class FlattenExceptionTest extends TestCase
$incomplete = unserialize('O:14:"BogusTestClass":0:{}');
$exception = $this->createException([
(object) ['foo' => 1],
$exception = $this->createException(array(
(object) array('foo' => 1),
new NotFoundHttpException(),
$incomplete,
$dh,
$fh,
function () {},
[1, 2],
['foo' => 123],
array(1, 2),
array('foo' => 123),
null,
true,
false,
@@ -260,7 +219,7 @@ class FlattenExceptionTest extends TestCase
'',
INF,
NAN,
]);
));
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
@@ -271,26 +230,26 @@ class FlattenExceptionTest extends TestCase
fclose($fh);
$i = 0;
$this->assertSame(['object', 'stdClass'], $array[$i++]);
$this->assertSame(['object', 'Symfony\Component\HttpKernel\Exception\NotFoundHttpException'], $array[$i++]);
$this->assertSame(['incomplete-object', 'BogusTestClass'], $array[$i++]);
$this->assertSame(['resource', 'stream'], $array[$i++]);
$this->assertSame(['resource', 'stream'], $array[$i++]);
$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', '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', [['integer', 1], ['integer', 2]]], $array[$i++]);
$this->assertSame(['array', ['foo' => ['integer', 123]]], $array[$i++]);
$this->assertSame(['null', null], $array[$i++]);
$this->assertSame(['boolean', true], $array[$i++]);
$this->assertSame(['boolean', false], $array[$i++]);
$this->assertSame(['integer', 0], $array[$i++]);
$this->assertSame(['float', 0.0], $array[$i++]);
$this->assertSame(['string', '0'], $array[$i++]);
$this->assertSame(['string', ''], $array[$i++]);
$this->assertSame(['float', INF], $array[$i++]);
$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');
@@ -300,7 +259,7 @@ class FlattenExceptionTest extends TestCase
public function testRecursionInArguments()
{
$a = null;
$a = ['foo', [2, &$a]];
$a = array('foo', array(2, &$a));
$exception = $this->createException($a);
$flattened = FlattenException::create($exception);
@@ -310,7 +269,7 @@ class FlattenExceptionTest extends TestCase
public function testTooBigArray()
{
$a = [];
$a = array();
for ($i = 0; $i < 20; ++$i) {
for ($j = 0; $j < 50; ++$j) {
for ($k = 0; $k < 10; ++$k) {
@@ -325,7 +284,7 @@ class FlattenExceptionTest extends TestCase
$flattened = FlattenException::create($exception);
$trace = $flattened->getTrace();
$this->assertSame($trace[1]['args'][0], ['array', ['array', '*SKIPPED over 10000 entries*']]);
$this->assertSame($trace[1]['args'][0], array('array', array('array', '*SKIPPED over 10000 entries*')));
$serializeTrace = serialize($trace);
@@ -333,54 +292,6 @@ class FlattenExceptionTest extends TestCase
$this->assertNotContains('*value1*', $serializeTrace);
}
public function testAnonymousClass()
{
$flattened = FlattenException::create(new class() extends \RuntimeException {
});
$this->assertSame('RuntimeException@anonymous', $flattened->getClass());
$flattened = FlattenException::create(new \Exception(sprintf('Class "%s" blah.', \get_class(new class() extends \RuntimeException {
}))));
$this->assertSame('Class "RuntimeException@anonymous" blah.', $flattened->getMessage());
}
public function testToStringEmptyMessage()
{
$exception = new \RuntimeException();
$flattened = FlattenException::create($exception);
$this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
$this->assertSame($exception->__toString(), $flattened->getAsString());
}
public function testToString()
{
$test = function ($a, $b, $c, $d) {
return new \RuntimeException('This is a test message');
};
$exception = $test('foo123', 1, null, 1.5);
$flattened = FlattenException::create($exception);
$this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
$this->assertSame($exception->__toString(), $flattened->getAsString());
}
public function testToStringParent()
{
$exception = new \LogicException('This is message 1');
$exception = new \RuntimeException('This is messsage 2', 500, $exception);
$flattened = FlattenException::create($exception);
$this->assertSame($exception->getTraceAsString(), $flattened->getTraceAsString());
$this->assertSame($exception->__toString(), $flattened->getAsString());
}
private function createException($foo)
{
return new \Exception();

View File

@@ -48,17 +48,8 @@ class ExceptionHandlerTest extends TestCase
$handler->sendPhpResponse(new \RuntimeException('Foo'));
$response = ob_get_clean();
$this->assertContains('<h1 class="break-long-words exception-message">Foo</h1>', $response);
$this->assertContains('Whoops, looks like something went wrong.', $response);
$this->assertContains('<div class="trace trace-as-html">', $response);
// taken from https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
$htmlWithXss = '<body onload=alert(\'test1\')> <b onmouseover=alert(\'Wufff!\')>click me!</b> <img src="j&#X41vascript:alert(\'test2\')"> <meta http-equiv="refresh"
content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg">';
ob_start();
$handler->sendPhpResponse(new \RuntimeException($htmlWithXss));
$response = ob_get_clean();
$this->assertContains(sprintf('<h1 class="break-long-words exception-message">%s</h1>', htmlspecialchars($htmlWithXss, ENT_COMPAT | ENT_SUBSTITUTE, 'UTF-8')), $response);
}
public function testStatusCode()
@@ -71,10 +62,10 @@ content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg"
$this->assertContains('Sorry, the page you are looking for could not be found.', $response);
$expectedHeaders = [
['HTTP/1.0 404', true, null],
['Content-Type: text/html; charset=iso8859-1', true, null],
];
$expectedHeaders = array(
array('HTTP/1.0 404', true, null),
array('Content-Type: text/html; charset=iso8859-1', true, null),
);
$this->assertSame($expectedHeaders, testHeader());
}
@@ -84,14 +75,14 @@ content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg"
$handler = new ExceptionHandler(false, 'iso8859-1');
ob_start();
$handler->sendPhpResponse(new MethodNotAllowedHttpException(['POST']));
$handler->sendPhpResponse(new MethodNotAllowedHttpException(array('POST')));
$response = ob_get_clean();
$expectedHeaders = [
['HTTP/1.0 405', true, null],
['Allow: POST', false, null],
['Content-Type: text/html; charset=iso8859-1', true, null],
];
$expectedHeaders = array(
array('HTTP/1.0 405', true, null),
array('Allow: POST', false, null),
array('Content-Type: text/html; charset=iso8859-1', true, null),
);
$this->assertSame($expectedHeaders, testHeader());
}
@@ -110,7 +101,7 @@ content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg"
{
$exception = new \Exception('foo');
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock();
$handler
->expects($this->exactly(2))
->method('sendPhpResponse');
@@ -128,7 +119,7 @@ content="0;url=data:text/html;base64,PHNjcmlwdD5hbGVydCgndGVzdDMnKTwvc2NyaXB0Pg"
{
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(array('sendPhpResponse'))->getMock();
$handler
->expects($this->once())
->method('sendPhpResponse');

View File

@@ -72,85 +72,85 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
$autoloader = new ComposerClassLoader();
$autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception'));
$debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
$debugClassLoader = new DebugClassLoader(array($autoloader, 'loadClass'));
return [
[
[
return array(
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'WhizBangFactory\' not found',
],
),
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\WhizBangFactory\' not found',
],
),
"Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\Bar\".\nDid you forget a \"use\" statement for another namespace?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'UndefinedFunctionException\' not found',
],
),
"Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony\Component\Debug\Exception\UndefinedFunctionException\"?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'PEARClass\' not found',
],
),
"Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"?",
],
[
[
),
array(
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(
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\"?",
[$autoloader, 'loadClass'],
],
[
[
array($autoloader, 'loadClass'),
),
array(
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\"?",
[$debugClassLoader, 'loadClass'],
],
[
[
array($debugClassLoader, 'loadClass'),
),
array(
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 another namespace?",
function ($className) { /* do nothing here */ },
],
];
),
);
}
public function testCannotRedeclareClass()
@@ -161,12 +161,12 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
require_once __DIR__.'/../FIXTURES2/REQUIREDTWICE.PHP';
$error = [
$error = array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Class \'Foo\\Bar\\RequiredTwice\' not found',
];
);
$handler = new ClassNotFoundFatalErrorHandler();
$exception = $handler->handleError($error, new FatalErrorException('', 0, $error['type'], $error['file'], $error['line']));

View File

@@ -35,44 +35,44 @@ class UndefinedFunctionFatalErrorHandlerTest extends TestCase
public function provideUndefinedFunctionData()
{
return [
[
[
return array(
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined function test_namespaced_function()',
],
),
"Attempted to call function \"test_namespaced_function\" from the global namespace.\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\test_namespaced_function()',
],
),
"Attempted to call function \"test_namespaced_function\" from namespace \"Foo\\Bar\\Baz\".\nDid you mean to call \"\\symfony\\component\\debug\\tests\\fatalerrorhandler\\test_namespaced_function\"?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined function foo()',
],
),
'Attempted to call function "foo" from the global namespace.',
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined function Foo\\Bar\\Baz\\foo()',
],
),
'Attempted to call function "foo" from namespace "Foo\Bar\Baz".',
],
];
),
);
}
}

View File

@@ -34,43 +34,43 @@ class UndefinedMethodFatalErrorHandlerTest extends TestCase
public function provideUndefinedMethodData()
{
return [
[
[
return array(
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::what()',
],
),
'Attempted to call an undefined method named "what" of class "SplObjectStorage".',
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::walid()',
],
),
"Attempted to call an undefined method named \"walid\" of class \"SplObjectStorage\".\nDid you mean to call \"valid\"?",
],
[
[
),
array(
array(
'type' => 1,
'line' => 12,
'file' => 'foo.php',
'message' => 'Call to undefined method SplObjectStorage::offsetFet()',
],
),
"Attempted to call an undefined method named \"offsetFet\" of class \"SplObjectStorage\".\nDid you mean to call e.g. \"offsetGet\", \"offsetSet\" or \"offsetUnset\"?",
],
[
[
'type' => 1,
'message' => 'Call to undefined method class@anonymous::test()',
'file' => '/home/possum/work/symfony/test.php',
'line' => 11,
],
),
array(
array(
'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,34 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class ClassWithAnnotatedParameters
{
/**
* @param string $foo this is a foo parameter
*/
public function fooMethod(string $foo)
{
}
/**
* @param string $bar parameter not implemented yet
*/
public function barMethod(/* string $bar = null */)
{
}
/**
* @param Quz $quz parameter not implemented yet
*/
public function quzMethod(/* Quz $quz = null */)
{
}
/**
* @param true $yes
*/
public function isSymfony()
{
}
}

View File

@@ -1,11 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
eval('
namespace Symfony\Component\Debug\Tests\Fixtures;
class DefinitionInEvaluatedCode
{
}
');

View File

@@ -1,22 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class ErrorHandlerThatUsesThePreviousOne
{
private static $previous;
public static function register()
{
$handler = new static();
self::$previous = set_error_handler([$handler, 'handleError']);
return $handler;
}
public function handleError($type, $message, $file, $line, $context)
{
return \call_user_func(self::$previous, $type, $message, $file, $line, $context);
}
}

View File

@@ -4,8 +4,6 @@ namespace Symfony\Component\Debug\Tests\Fixtures;
class ExtendedFinalMethod extends FinalMethod
{
use FinalMethod2Trait;
/**
* {@inheritdoc}
*/

View File

@@ -3,8 +3,8 @@
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @method string traitMethod()
* @final
*/
trait VirtualTrait
class FinalClass
{
}

View File

@@ -1,85 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @final since version 3.3.
*/
class FinalClass1
{
// simple comment
}
/**
* @final
*/
class FinalClass2
{
// no comment
}
/**
* @final comment with @@@ and ***
*
* @author John Doe
*/
class FinalClass3
{
// with comment and a tag after
}
/**
* @final
*
* @author John Doe
*/
class FinalClass4
{
// without comment and a tag after
}
/**
* @author John Doe
*
*
* @final multiline
* comment
*/
class FinalClass5
{
// with comment and a tag before
}
/**
* @author John Doe
*
* @final
*/
class FinalClass6
{
// without comment and a tag before
}
/**
* @author John Doe
*
* @final another
* multiline comment...
*
* @return string
*/
class FinalClass7
{
// with comment and a tag before and after
}
/**
* @author John Doe
* @final
*
* @return string
*/
class FinalClass8
{
// without comment and a tag before and after
}

View File

@@ -11,15 +11,6 @@ class FinalMethod
{
}
/**
* @final
*
* @return int
*/
public function finalMethod2()
{
}
public function anotherMethod()
{
}

View File

@@ -1,10 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
trait FinalMethod2Trait
{
public function finalMethod2()
{
}
}

View File

@@ -1,27 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* Ensures a deprecation is triggered when a new parameter is not declared in child classes.
*/
interface InterfaceWithAnnotatedParameters
{
/**
* @param bool $matrix
*/
public function whereAmI();
/**
* @param $noType
* @param callable(\Throwable|null $reason, mixed $value) $callback and a comment
* about this great param
* @param string $param (comment with $dollar)
* @param $defined
* @param callable ($a, $b) $anotherOne
* @param callable (mixed $a, $b) $definedCallable
* @param Type$WithDollarIsStillAType $ccc
* @param \JustAType
*/
public function iAmHere();
}

View File

@@ -1,15 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
use Symfony\Component\Debug\BufferingLogger;
class LoggerThatSetAnErrorHandler extends BufferingLogger
{
public function log($level, $message, array $context = [])
{
set_error_handler('is_string');
parent::log($level, $message, $context);
restore_error_handler();
}
}

View File

@@ -1,32 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
class SubClassWithAnnotatedParameters extends ClassWithAnnotatedParameters implements InterfaceWithAnnotatedParameters
{
use TraitWithAnnotatedParameters;
public function fooMethod(string $foo)
{
}
public function barMethod($bar = null)
{
}
public function quzMethod()
{
}
public function whereAmI()
{
}
/**
* @param $defined
* @param Type\Does\Not\Matter $definedCallable
*/
public function iAmHere()
{
}
}

View File

@@ -18,7 +18,7 @@ class ToStringThrower
} catch (\Exception $e) {
// Using user_error() here is on purpose so we do not forget
// that this alias also should work alongside with trigger_error().
return trigger_error($e, E_USER_ERROR);
return user_error($e, E_USER_ERROR);
}
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
trait TraitWithAnnotatedParameters
{
/**
* `@param` annotations in traits are not parsed.
*/
public function isSymfony()
{
}
}

View File

@@ -1,13 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
trait TraitWithInternalMethod
{
/**
* @internal
*/
public function foo()
{
}
}

View File

@@ -1,11 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @method string classMethod()
*/
class VirtualClass
{
use VirtualTrait;
}

View File

@@ -1,18 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @method string magicMethod()
* @method static string staticMagicMethod()
*/
class VirtualClassMagicCall
{
public static function __callStatic($name, $arguments)
{
}
public function __call($name, $arguments)
{
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @method string interfaceMethod()
* @method sameLineInterfaceMethod($arg)
* @method sameLineInterfaceMethodNoBraces
*
* Ignored
* @method
* @method
*
* Not ignored
* @method newLineInterfaceMethod() Some description!
* @method \stdClass newLineInterfaceMethodNoBraces Description
*
* Invalid
* @method unknownType invalidInterfaceMethod()
* @method unknownType|string invalidInterfaceMethodNoBraces
*
* Complex
* @method complexInterfaceMethod($arg, ...$args)
* @method string[]|int complexInterfaceMethodTyped($arg, int ...$args) Description ...
*
* Static
* @method static Foo&Bar staticMethod()
* @method static staticMethodNoBraces
* @method static \stdClass staticMethodTyped(int $arg) Description
* @method static \stdClass[] staticMethodTypedNoBraces
*/
interface VirtualInterface
{
}

View File

@@ -1,10 +0,0 @@
<?php
namespace Symfony\Component\Debug\Tests\Fixtures;
/**
* @method string subInterfaceMethod()
*/
interface VirtualSubInterface extends VirtualInterface
{
}

View File

@@ -25,11 +25,11 @@ namespace Symfony\Component\Debug\Tests;
function testHeader()
{
static $headers = [];
static $headers = array();
if (!$h = \func_get_args()) {
$h = $headers;
$headers = [];
$headers = array();
return $h;
}

View File

@@ -24,4 +24,3 @@ class_exists(ExtendedFinalMethod::class);
?>
--EXPECTF--
The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod()" method is considered final. It may change without further notice as of its next major version. You should not extend it from "Symfony\Component\Debug\Tests\Fixtures\ExtendedFinalMethod".
The "Symfony\Component\Debug\Tests\Fixtures\FinalMethod::finalMethod2()" method is considered final. 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,7 +1,5 @@
--TEST--
Test catching fatal errors when handlers are nested
--INI--
display_errors=0
--FILE--
<?php
@@ -26,6 +24,7 @@ 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

@@ -14,7 +14,7 @@ require $vendor.'/vendor/autoload.php';
if (true) {
class TestLogger extends \Psr\Log\AbstractLogger
{
public function log($level, $message, array $context = [])
public function log($level, $message, array $context = array())
{
echo $message, "\n";
}

View File

@@ -17,14 +17,14 @@ ini_set('display_errors', 0);
$eHandler = set_error_handler('var_dump');
$xHandler = set_exception_handler('var_dump');
var_dump([
var_dump(array(
$eHandler[0] === $xHandler[0] ? 'Error and exception handlers do match' : 'Error and exception handlers are different',
]);
));
$eHandler[0]->setExceptionHandler('print_r');
if (true) {
class Broken implements \JsonSerializable
class Broken implements \Serializable
{
}
}
@@ -37,6 +37,6 @@ array(1) {
}
object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) {
["message":protected]=>
string(179) "Error: Class Symfony\Component\Debug\Broken contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (JsonSerializable::jsonSerialize)"
string(199) "Error: Class Symfony\Component\Debug\Broken contains 2 abstract methods and must therefore be declared abstract or implement the remaining methods (Serializable::serialize, Serializable::unserialize)"
%a
}

View File

@@ -34,7 +34,7 @@
"minimum-stability": "dev",
"extra": {
"branch-alias": {
"dev-master": "4.3-dev"
"dev-master": "4.0-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/5.2/phpunit.xsd"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
backupGlobals="false"
colors="true"
bootstrap="vendor/autoload.php"