mirror of
https://github.com/symfony/debug.git
synced 2026-03-25 09:42:20 +01:00
Compare commits
154 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e36299635 | ||
|
|
1eff904a95 | ||
|
|
70dd18e93b | ||
|
|
e45fa0c74a | ||
|
|
9a81b6360b | ||
|
|
cbe127b719 | ||
|
|
c533fe8d14 | ||
|
|
ba2ce2f0ee | ||
|
|
9d32af2d83 | ||
|
|
7793eea884 | ||
|
|
e8d70531f3 | ||
|
|
5ea9c3e019 | ||
|
|
f72e33fdb1 | ||
|
|
cc5c1efd0e | ||
|
|
b3e7ce815d | ||
|
|
c1a032ce81 | ||
|
|
afcdea44a2 | ||
|
|
ed30cb9da5 | ||
|
|
0b60030091 | ||
|
|
2d8ff49ce0 | ||
|
|
2ab93347e8 | ||
|
|
692aa71df2 | ||
|
|
4645282605 | ||
|
|
93fd0e9f20 | ||
|
|
3ec8596803 | ||
|
|
19056a4079 | ||
|
|
12e1811d2b | ||
|
|
c5366e87da | ||
|
|
406bb25021 | ||
|
|
a78bf4dc3a | ||
|
|
dda62cdc3a | ||
|
|
4dc880d082 | ||
|
|
288013687b | ||
|
|
63f26f1891 | ||
|
|
91730bdf7d | ||
|
|
bef2cd01a1 | ||
|
|
1fd323489f | ||
|
|
22fea487fa | ||
|
|
8ff6ff5f51 | ||
|
|
89429ed0eb | ||
|
|
527887c385 | ||
|
|
f832ecacfb | ||
|
|
bc977cb268 | ||
|
|
340a0fc6ed | ||
|
|
dabd21d13b | ||
|
|
b6f28caaf2 | ||
|
|
5bf4824968 | ||
|
|
fb91321bee | ||
|
|
2fdd843f5e | ||
|
|
dff676526c | ||
|
|
b49ea98332 | ||
|
|
5f3e892c1c | ||
|
|
7a65fe00f9 | ||
|
|
a808f15333 | ||
|
|
32d260af46 | ||
|
|
8ab610f0b0 | ||
|
|
8a212666c0 | ||
|
|
740602e8b0 | ||
|
|
8ca4340b26 | ||
|
|
b6d3ae55c0 | ||
|
|
d58c0d65d7 | ||
|
|
d8f4fb3815 | ||
|
|
bb91fa5ab9 | ||
|
|
1172dc1abe | ||
|
|
a30d8fe728 | ||
|
|
7d9d22bcd0 | ||
|
|
d0bff29e62 | ||
|
|
7e1a4ec082 | ||
|
|
4e025104f1 | ||
|
|
ceed75ff0d | ||
|
|
e79bbe15d8 | ||
|
|
97cde06d79 | ||
|
|
22b4d033e6 | ||
|
|
671fc55bd1 | ||
|
|
bcb342fd9c | ||
|
|
2d279b6bb1 | ||
|
|
681afbb264 | ||
|
|
dba19a100b | ||
|
|
f90c108a59 | ||
|
|
63b4ddb7c6 | ||
|
|
85c2c0c26f | ||
|
|
c4d2a5ad16 | ||
|
|
032e6624c3 | ||
|
|
91691fa245 | ||
|
|
2a9e82faff | ||
|
|
43ce8ab34c | ||
|
|
adbdd5d663 | ||
|
|
6d4484cd4b | ||
|
|
94eda15aa1 | ||
|
|
a5961253fa | ||
|
|
2372d7084d | ||
|
|
26bccc04be | ||
|
|
878d05a2eb | ||
|
|
5f14d80d8b | ||
|
|
83e5508e9d | ||
|
|
d5f9980171 | ||
|
|
2533ffde4d | ||
|
|
806e831d70 | ||
|
|
de73f48977 | ||
|
|
8d8a9e877b | ||
|
|
a850962740 | ||
|
|
9359ad70a1 | ||
|
|
1fe36a98e2 | ||
|
|
c6af9101fb | ||
|
|
4792c7baab | ||
|
|
091fd557df | ||
|
|
945530598e | ||
|
|
8b8b8b7e57 | ||
|
|
f47ba47df3 | ||
|
|
8f517eb954 | ||
|
|
c7ac313d92 | ||
|
|
97a066c39a | ||
|
|
4b3f518420 | ||
|
|
cf9b2e33f7 | ||
|
|
643df31c6a | ||
|
|
7ae7883ea2 | ||
|
|
7cdd4f6f12 | ||
|
|
f49c101672 | ||
|
|
9f1401aebf | ||
|
|
2e1483cd0d | ||
|
|
f6bdc0a7df | ||
|
|
00db8f8b37 | ||
|
|
789a7a9031 | ||
|
|
1a53cddc50 | ||
|
|
b04503aa3b | ||
|
|
42fa73a995 | ||
|
|
c0ed02a95c | ||
|
|
64cb33c81e | ||
|
|
7f2f295f38 | ||
|
|
c8e078dd18 | ||
|
|
170e2f8f61 | ||
|
|
e597d2960c | ||
|
|
e0a2b92ee0 | ||
|
|
fbd644bb30 | ||
|
|
7a8a60421b | ||
|
|
0aae90af4e | ||
|
|
85c1365d7a | ||
|
|
436274c5b8 | ||
|
|
e72d01486a | ||
|
|
92320d007e | ||
|
|
0a6c85e5c6 | ||
|
|
8f69eb64e4 | ||
|
|
acb9e69323 | ||
|
|
9374383ad2 | ||
|
|
42bdc01d61 | ||
|
|
e2b31a5a23 | ||
|
|
c16ccd52db | ||
|
|
32023e0ffa | ||
|
|
056f617413 | ||
|
|
d49a626c51 | ||
|
|
1970189082 | ||
|
|
ef8bd62973 | ||
|
|
09f5c0938b | ||
|
|
feb747f95f |
@@ -1,6 +1,14 @@
|
||||
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
|
||||
-----
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
namespace Symfony\Component\Debug;
|
||||
|
||||
use PHPUnit\Framework\MockObject\Matcher\StatelessInvocation;
|
||||
|
||||
/**
|
||||
* Autoloader checking if the class is really defined in the file found.
|
||||
*
|
||||
@@ -21,6 +23,7 @@ namespace Symfony\Component\Debug;
|
||||
* @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
|
||||
{
|
||||
@@ -34,7 +37,9 @@ class DebugClassLoader
|
||||
private static $deprecated = [];
|
||||
private static $internal = [];
|
||||
private static $internalMethods = [];
|
||||
private static $annotatedParameters = [];
|
||||
private static $darwinCache = ['/' => ['/', []]];
|
||||
private static $method = [];
|
||||
|
||||
public function __construct(callable $classLoader)
|
||||
{
|
||||
@@ -148,11 +153,11 @@ class DebugClassLoader
|
||||
if (!$file = $this->classLoader[0]->findFile($class) ?: false) {
|
||||
// no-op
|
||||
} elseif (\function_exists('opcache_is_script_cached') && @opcache_is_script_cached($file)) {
|
||||
require $file;
|
||||
include $file;
|
||||
|
||||
return;
|
||||
} else {
|
||||
require $file;
|
||||
} elseif (false === include $file) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
($this->classLoader)($class);
|
||||
@@ -167,7 +172,7 @@ class DebugClassLoader
|
||||
|
||||
private function checkClass($class, $file = null)
|
||||
{
|
||||
$exists = null === $file || \class_exists($class, false) || \interface_exists($class, false) || \trait_exists($class, false);
|
||||
$exists = null === $file || class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false);
|
||||
|
||||
if (null !== $file && $class && '\\' === $class[0]) {
|
||||
$class = substr($class, 1);
|
||||
@@ -185,7 +190,7 @@ class DebugClassLoader
|
||||
}
|
||||
$name = $refl->getName();
|
||||
|
||||
if ($name !== $class && 0 === \strcasecmp($name, $class)) {
|
||||
if ($name !== $class && 0 === strcasecmp($name, $class)) {
|
||||
throw new \RuntimeException(sprintf('Case mismatch between loaded and declared class names: "%s" vs "%s".', $class, $name));
|
||||
}
|
||||
|
||||
@@ -218,23 +223,41 @@ class DebugClassLoader
|
||||
$deprecations = [];
|
||||
|
||||
// Don't trigger deprecations for classes in the same vendor
|
||||
if (2 > $len = 1 + (\strpos($class, '\\') ?: \strpos($class, '_'))) {
|
||||
if (2 > $len = 1 + (strpos($class, '\\') ?: strpos($class, '_'))) {
|
||||
$len = 0;
|
||||
$ns = '';
|
||||
} else {
|
||||
$ns = \str_replace('_', '\\', \substr($class, 0, $len));
|
||||
$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+\*(?: @|/$)#s', $doc, $notice)) {
|
||||
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);
|
||||
$parent = get_parent_class($class);
|
||||
$parentAndOwnInterfaces = $this->getOwnInterfaces($class, $parent);
|
||||
if ($parent) {
|
||||
$parentAndOwnInterfaces[$parent] = $parent;
|
||||
@@ -249,30 +272,53 @@ class DebugClassLoader
|
||||
}
|
||||
|
||||
// Detect if the parent is annotated
|
||||
foreach ($parentAndOwnInterfaces + \class_uses($class, false) as $use) {
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
if (trait_exists($class)) {
|
||||
return $deprecations;
|
||||
}
|
||||
|
||||
// Inherit @final and @internal annotations for methods
|
||||
// Inherit @final, @internal and @param annotations for methods
|
||||
self::$finalMethods[$class] = [];
|
||||
self::$internalMethods[$class] = [];
|
||||
self::$annotatedParameters[$class] = [];
|
||||
foreach ($parentAndOwnInterfaces as $use) {
|
||||
foreach (['finalMethods', 'internalMethods'] as $property) {
|
||||
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];
|
||||
}
|
||||
@@ -291,20 +337,57 @@ class DebugClassLoader
|
||||
|
||||
if (isset(self::$internalMethods[$class][$method->name])) {
|
||||
list($declaringClass, $message) = self::$internalMethods[$class][$method->name];
|
||||
if (\strncmp($ns, $declaringClass, $len)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Detect method annotations
|
||||
if (false === $doc = $method->getDocComment()) {
|
||||
// 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+\*(?: @|/$)#s', $doc, $notice)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,6 +395,12 @@ class DebugClassLoader
|
||||
return $deprecations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $class
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function checkCase(\ReflectionClass $refl, $file, $class)
|
||||
{
|
||||
$real = explode('\\', $class.strrchr($file, '.'));
|
||||
@@ -328,7 +417,7 @@ class DebugClassLoader
|
||||
array_splice($tail, 0, $i + 1);
|
||||
|
||||
if (!$tail) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$tail = \DIRECTORY_SEPARATOR.implode(\DIRECTORY_SEPARATOR, $tail);
|
||||
@@ -344,6 +433,8 @@ class DebugClassLoader
|
||||
) {
|
||||
return [substr($tail, -$tailLen + 1), substr($real, -$tailLen + 1), substr($real, 0, -$tailLen + 1)];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -364,7 +455,11 @@ class DebugClassLoader
|
||||
$real = self::$darwinCache[$kDir][0];
|
||||
} else {
|
||||
$dir = getcwd();
|
||||
chdir($real);
|
||||
|
||||
if (!@chdir($real)) {
|
||||
return $real.$file;
|
||||
}
|
||||
|
||||
$real = getcwd().'/';
|
||||
chdir($dir);
|
||||
|
||||
@@ -385,8 +480,13 @@ class DebugClassLoader
|
||||
|
||||
$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];
|
||||
return $real.$dirFiles[$file];
|
||||
}
|
||||
|
||||
$kFile = strtolower($file);
|
||||
@@ -405,7 +505,7 @@ class DebugClassLoader
|
||||
self::$darwinCache[$kDir][1] = $dirFiles;
|
||||
}
|
||||
|
||||
return $real .= $dirFiles[$kFile];
|
||||
return $real.$dirFiles[$kFile];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ 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;
|
||||
@@ -44,6 +45,8 @@ 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
|
||||
{
|
||||
@@ -219,7 +222,7 @@ class ErrorHandler
|
||||
}
|
||||
if (!\is_array($log)) {
|
||||
$log = [$log];
|
||||
} elseif (!array_key_exists(0, $log)) {
|
||||
} elseif (!\array_key_exists(0, $log)) {
|
||||
throw new \InvalidArgumentException('No logger provided');
|
||||
}
|
||||
if (null === $log[0]) {
|
||||
@@ -379,6 +382,10 @@ class ErrorHandler
|
||||
*/
|
||||
public function handleError($type, $message, $file, $line)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70300 && E_WARNING === $type && '"' === $message[0] && false !== strpos($message, '" targeting switch is equivalent to "break')) {
|
||||
$type = E_DEPRECATED;
|
||||
}
|
||||
|
||||
// Level is the current error reporting level to manage silent error.
|
||||
$level = error_reporting();
|
||||
$silenced = 0 === ($level & $type);
|
||||
@@ -405,15 +412,19 @@ class ErrorHandler
|
||||
$context = $e;
|
||||
}
|
||||
|
||||
$logMessage = $this->levels[$type].': '.$message;
|
||||
if (false !== strpos($message, "class@anonymous\0")) {
|
||||
$logMessage = $this->levels[$type].': '.(new FlattenException())->setMessage($message)->getMessage();
|
||||
} else {
|
||||
$logMessage = $this->levels[$type].': '.$message;
|
||||
}
|
||||
|
||||
if (null !== self::$toStringException) {
|
||||
$errorAsException = self::$toStringException;
|
||||
self::$toStringException = null;
|
||||
} elseif (!$throw && !($type & $level)) {
|
||||
if (!isset(self::$silencedErrorCache[$id = $file.':'.$line])) {
|
||||
$lightTrace = $this->tracedErrors & $type ? $this->cleanTrace(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3), $type, $file, $line, false) : [];
|
||||
$errorAsException = new SilencedErrorContext($type, $file, $line, $lightTrace);
|
||||
$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);
|
||||
} elseif (isset(self::$silencedErrorCache[$id][$message])) {
|
||||
$lightTrace = null;
|
||||
$errorAsException = self::$silencedErrorCache[$id][$message];
|
||||
@@ -431,12 +442,11 @@ class ErrorHandler
|
||||
self::$silencedErrorCache[$id][$message] = $errorAsException;
|
||||
}
|
||||
if (null === $lightTrace) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
} 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);
|
||||
@@ -448,7 +458,7 @@ class ErrorHandler
|
||||
}
|
||||
|
||||
if ($throw) {
|
||||
if (E_USER_ERROR & $type) {
|
||||
if (\PHP_VERSION_ID < 70400 && E_USER_ERROR & $type) {
|
||||
for ($i = 1; isset($backtrace[$i]); ++$i) {
|
||||
if (isset($backtrace[$i]['function'], $backtrace[$i]['type'], $backtrace[$i - 1]['function'])
|
||||
&& '__toString' === $backtrace[$i]['function']
|
||||
@@ -486,6 +496,11 @@ 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;
|
||||
@@ -494,7 +509,7 @@ class ErrorHandler
|
||||
$this->isRecursive = false;
|
||||
|
||||
if (!\defined('HHVM_VERSION')) {
|
||||
set_error_handler([$this, __FUNCTION__]);
|
||||
set_error_handler($currentErrorHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -522,21 +537,24 @@ 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 = [
|
||||
'type' => $type,
|
||||
'message' => $message = $exception->getMessage(),
|
||||
'message' => $message,
|
||||
'file' => $exception->getFile(),
|
||||
'line' => $exception->getLine(),
|
||||
];
|
||||
} else {
|
||||
$message = 'Fatal '.$exception->getMessage();
|
||||
$message = 'Fatal '.$message;
|
||||
}
|
||||
} elseif ($exception instanceof \ErrorException) {
|
||||
$message = 'Uncaught '.$exception->getMessage();
|
||||
$message = 'Uncaught '.$message;
|
||||
} else {
|
||||
$message = 'Uncaught Exception: '.$exception->getMessage();
|
||||
$message = 'Uncaught Exception: '.$message;
|
||||
}
|
||||
}
|
||||
if ($this->loggedErrors & $type) {
|
||||
@@ -557,7 +575,9 @@ class ErrorHandler
|
||||
$this->exceptionHandler = null;
|
||||
try {
|
||||
if (null !== $exceptionHandler) {
|
||||
return $exceptionHandler($exception);
|
||||
$exceptionHandler($exception);
|
||||
|
||||
return;
|
||||
}
|
||||
$handlerException = $handlerException ?: $exception;
|
||||
} catch (\Throwable $handlerException) {
|
||||
@@ -665,6 +685,9 @@ class ErrorHandler
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -675,6 +698,13 @@ 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']);
|
||||
|
||||
@@ -27,6 +27,7 @@ class FlattenException
|
||||
private $code;
|
||||
private $previous;
|
||||
private $trace;
|
||||
private $traceAsString;
|
||||
private $class;
|
||||
private $statusCode;
|
||||
private $headers;
|
||||
@@ -90,9 +91,14 @@ class FlattenException
|
||||
return $this->statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setStatusCode($code)
|
||||
{
|
||||
$this->statusCode = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHeaders()
|
||||
@@ -100,9 +106,14 @@ class FlattenException
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setHeaders(array $headers)
|
||||
{
|
||||
$this->headers = $headers;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
@@ -110,9 +121,14 @@ class FlattenException
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setClass($class)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFile()
|
||||
@@ -120,9 +136,14 @@ class FlattenException
|
||||
return $this->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setFile($file)
|
||||
{
|
||||
$this->file = $file;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLine()
|
||||
@@ -130,9 +151,14 @@ class FlattenException
|
||||
return $this->line;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setLine($line)
|
||||
{
|
||||
$this->line = $line;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMessage()
|
||||
@@ -140,9 +166,20 @@ 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.*?\.php(?:0x?|:[0-9]++\$)[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()
|
||||
@@ -150,9 +187,14 @@ class FlattenException
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setCode($code)
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPrevious()
|
||||
@@ -160,9 +202,14 @@ class FlattenException
|
||||
return $this->previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setPrevious(self $previous)
|
||||
{
|
||||
$this->previous = $previous;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllPrevious()
|
||||
@@ -191,11 +238,16 @@ class FlattenException
|
||||
$this->setTraceFromThrowable($exception);
|
||||
}
|
||||
|
||||
public function setTraceFromThrowable(\Throwable $throwable): void
|
||||
public function setTraceFromThrowable(\Throwable $throwable)
|
||||
{
|
||||
$this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
|
||||
$this->traceAsString = $throwable->getTraceAsString();
|
||||
|
||||
return $this->setTrace($throwable->getTrace(), $throwable->getFile(), $throwable->getLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function setTrace($trace, $file, $line)
|
||||
{
|
||||
$this->trace = [];
|
||||
@@ -229,6 +281,8 @@ class FlattenException
|
||||
'args' => isset($entry['args']) ? $this->flattenArgs($entry['args']) : [],
|
||||
];
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function flattenArgs($args, $level = 0, &$count = 0)
|
||||
@@ -273,4 +327,33 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ class SilencedErrorContext implements \JsonSerializable
|
||||
return $this->trace;
|
||||
}
|
||||
|
||||
public function JsonSerialize()
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'severity' => $this->severity,
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -33,11 +33,11 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$notFoundSuffix = '\' not found';
|
||||
$notFoundSuffixLen = \strlen($notFoundSuffix);
|
||||
if ($notFoundSuffixLen > $messageLen) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (['class', 'interface', 'trait'] as $typeName) {
|
||||
@@ -71,6 +71,8 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
|
||||
return new ClassNotFoundException($message, $exception);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +173,11 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
}
|
||||
}
|
||||
|
||||
require_once $file;
|
||||
try {
|
||||
require_once $file;
|
||||
} catch (\Throwable $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if ($this->classExists($candidate)) {
|
||||
|
||||
@@ -30,17 +30,17 @@ class UndefinedFunctionFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
$notFoundSuffix = '()';
|
||||
$notFoundSuffixLen = \strlen($notFoundSuffix);
|
||||
if ($notFoundSuffixLen > $messageLen) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$prefix = 'Call to undefined function ';
|
||||
$prefixLen = \strlen($prefix);
|
||||
if (0 !== strpos($error['message'], $prefix)) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$fullyQualifiedFunctionName = substr($error['message'], $prefixLen, -$notFoundSuffixLen);
|
||||
|
||||
@@ -28,7 +28,7 @@ class UndefinedMethodFatalErrorHandler implements FatalErrorHandlerInterface
|
||||
{
|
||||
preg_match('/^Call to undefined method (.*)::(.*)\(\)$/', $error['message'], $matches);
|
||||
if (!$matches) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
$className = $matches[1];
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2019 Fabien Potencier
|
||||
Copyright (c) 2004-2020 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
|
||||
|
||||
@@ -6,7 +6,7 @@ The Debug component provides tools to ease debugging PHP code.
|
||||
Resources
|
||||
---------
|
||||
|
||||
* [Documentation](https://symfony.com/doc/current/components/debug/index.html)
|
||||
* [Documentation](https://symfony.com/doc/current/components/debug.html)
|
||||
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
|
||||
* [Report issues](https://github.com/symfony/symfony/issues) and
|
||||
[send Pull Requests](https://github.com/symfony/symfony/pulls)
|
||||
|
||||
@@ -23,7 +23,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
|
||||
private $loader;
|
||||
|
||||
protected function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->errorReporting = error_reporting(E_ALL);
|
||||
$this->loader = new ClassLoader();
|
||||
@@ -31,7 +31,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
DebugClassLoader::enable();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
DebugClassLoader::disable();
|
||||
spl_autoload_unregister([$this->loader, 'loadClass']);
|
||||
@@ -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);
|
||||
@@ -58,49 +58,43 @@ class DebugClassLoaderTest extends TestCase
|
||||
$this->fail('DebugClassLoader did not register');
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
* @expectedExceptionMessage boo
|
||||
*/
|
||||
public function testThrowingClass()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$this->expectExceptionMessage('boo');
|
||||
try {
|
||||
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
|
||||
class_exists(Fixtures\Throwing::class);
|
||||
$this->fail('Exception expected');
|
||||
} catch (\Exception $e) {
|
||||
$this->assertSame('boo', $e->getMessage());
|
||||
}
|
||||
|
||||
// the second call also should throw
|
||||
class_exists(__NAMESPACE__.'\Fixtures\Throwing');
|
||||
class_exists(Fixtures\Throwing::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testNameCaseMismatch()
|
||||
{
|
||||
class_exists(__NAMESPACE__.'\TestingCaseMismatch', true);
|
||||
$this->expectException('RuntimeException');
|
||||
$this->expectExceptionMessage('Case mismatch between loaded and declared class names');
|
||||
class_exists(TestingCaseMismatch::class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
* @expectedExceptionMessage Case mismatch between class and real file names
|
||||
*/
|
||||
public function testFileCaseMismatch()
|
||||
{
|
||||
$this->expectException('RuntimeException');
|
||||
$this->expectExceptionMessage('Case mismatch between class and real file names');
|
||||
if (!file_exists(__DIR__.'/Fixtures/CaseMismatch.php')) {
|
||||
$this->markTestSkipped('Can only be run on case insensitive filesystems');
|
||||
}
|
||||
|
||||
class_exists(__NAMESPACE__.'\Fixtures\CaseMismatch', true);
|
||||
class_exists(Fixtures\CaseMismatch::class, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \RuntimeException
|
||||
*/
|
||||
public function testPsr4CaseMismatch()
|
||||
{
|
||||
$this->expectException('RuntimeException');
|
||||
$this->expectExceptionMessage('Case mismatch between loaded and declared class names');
|
||||
class_exists(__NAMESPACE__.'\Fixtures\Psr4CaseMismatch', true);
|
||||
}
|
||||
|
||||
@@ -116,7 +110,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
|
||||
public function testClassAlias()
|
||||
{
|
||||
$this->assertTrue(class_exists(__NAMESPACE__.'\Fixtures\ClassAlias', true));
|
||||
$this->assertTrue(class_exists(Fixtures\ClassAlias::class, true));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +152,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_NOTICE);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\NonDeprecatedInterfaceClass', true);
|
||||
class_exists('Test\\'.NonDeprecatedInterfaceClass::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
@@ -205,7 +199,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
require __DIR__.'/Fixtures/FinalClasses.php';
|
||||
|
||||
$i = 1;
|
||||
while(class_exists($finalClass = __NAMESPACE__.'\\Fixtures\\FinalClass'.$i++, false)) {
|
||||
while (class_exists($finalClass = Fixtures\FinalClass::class.$i++, false)) {
|
||||
spl_autoload_call($finalClass);
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\Extends'.substr($finalClass, strrpos($finalClass, '\\') + 1), true);
|
||||
}
|
||||
@@ -231,7 +225,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
|
||||
$e = error_reporting(E_USER_DEPRECATED);
|
||||
|
||||
class_exists(__NAMESPACE__.'\\Fixtures\\ExtendedFinalMethod', true);
|
||||
class_exists(Fixtures\ExtendedFinalMethod::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
@@ -250,7 +244,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
$e = error_reporting(0);
|
||||
trigger_error('', E_USER_NOTICE);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsAnnotatedClass', true);
|
||||
class_exists('Test\\'.ExtendsAnnotatedClass::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
@@ -267,7 +261,7 @@ class DebugClassLoaderTest extends TestCase
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
|
||||
$e = error_reporting(E_USER_DEPRECATED);
|
||||
|
||||
class_exists('Test\\'.__NAMESPACE__.'\\ExtendsInternals', true);
|
||||
class_exists('Test\\'.ExtendsInternals::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
@@ -280,19 +274,87 @@ class DebugClassLoaderTest extends TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function testExtendedMethodDefinesNewParameters()
|
||||
{
|
||||
$deprecations = [];
|
||||
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
|
||||
$e = error_reporting(E_USER_DEPRECATED);
|
||||
|
||||
class_exists(Fixtures\SubClassWithAnnotatedParameters::class, 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);
|
||||
class_exists('Test\\'.UseTraitWithInternalMethod::class, 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\\'.ExtendsVirtual::class, 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\\'.ExtendsVirtualMagicCall::class, true);
|
||||
|
||||
error_reporting($e);
|
||||
restore_error_handler();
|
||||
|
||||
$this->assertSame([], $deprecations);
|
||||
}
|
||||
|
||||
public function testEvaluatedCode()
|
||||
{
|
||||
$this->assertTrue(class_exists(Fixtures\DefinitionInEvaluatedCode::class, true));
|
||||
}
|
||||
}
|
||||
|
||||
class ClassLoader
|
||||
@@ -308,47 +370,75 @@ class ClassLoader
|
||||
|
||||
public function findFile($class)
|
||||
{
|
||||
$fixtureDir = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'.DIRECTORY_SEPARATOR;
|
||||
$fixtureDir = __DIR__.\DIRECTORY_SEPARATOR.'Fixtures'.\DIRECTORY_SEPARATOR;
|
||||
|
||||
if (__NAMESPACE__.'\TestingUnsilencing' === $class) {
|
||||
if (TestingUnsilencing::class === $class) {
|
||||
eval('-- parse error --');
|
||||
} elseif (__NAMESPACE__.'\TestingStacking' === $class) {
|
||||
} elseif (TestingStacking::class === $class) {
|
||||
eval('namespace '.__NAMESPACE__.'; class TestingStacking { function foo() {} }');
|
||||
} elseif (__NAMESPACE__.'\TestingCaseMismatch' === $class) {
|
||||
} elseif (TestingCaseMismatch::class === $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) {
|
||||
return $fixtureDir.'notPsr0Bis.php';
|
||||
} elseif ('Symfony\Bridge\Debug\Tests\Fixtures\ExtendsDeprecatedParent' === $class) {
|
||||
eval('namespace Symfony\Bridge\Debug\Tests\Fixtures; class ExtendsDeprecatedParent extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedParentClass' === $class) {
|
||||
} elseif ('Test\\'.DeprecatedParentClass::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedParentClass extends \\'.__NAMESPACE__.'\Fixtures\DeprecatedClass {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\DeprecatedInterfaceClass' === $class) {
|
||||
} elseif ('Test\\'.DeprecatedInterfaceClass::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class DeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\DeprecatedInterface {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\NonDeprecatedInterfaceClass' === $class) {
|
||||
} elseif ('Test\\'.NonDeprecatedInterfaceClass::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class NonDeprecatedInterfaceClass implements \\'.__NAMESPACE__.'\Fixtures\NonDeprecatedInterface {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\Float' === $class) {
|
||||
} elseif ('Test\\'.Float::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class Float {}');
|
||||
} elseif (0 === strpos($class, 'Test\\'.__NAMESPACE__.'\ExtendsFinalClass')) {
|
||||
} elseif (0 === strpos($class, 'Test\\'.ExtendsFinalClass::class)) {
|
||||
$classShortName = substr($class, strrpos($class, '\\') + 1);
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class '.$classShortName.' extends \\'.__NAMESPACE__.'\Fixtures\\'.substr($classShortName, 7).' {}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsAnnotatedClass' === $class) {
|
||||
} elseif ('Test\\'.ExtendsAnnotatedClass::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsAnnotatedClass extends \\'.__NAMESPACE__.'\Fixtures\AnnotatedClass {
|
||||
public function deprecatedMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternals' === $class) {
|
||||
} elseif ('Test\\'.ExtendsInternals::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternals extends ExtendsInternalsParent {
|
||||
use \\'.__NAMESPACE__.'\Fixtures\InternalTrait;
|
||||
|
||||
public function internalMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\ExtendsInternalsParent' === $class) {
|
||||
} elseif ('Test\\'.ExtendsInternalsParent::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsInternalsParent extends \\'.__NAMESPACE__.'\Fixtures\InternalClass implements \\'.__NAMESPACE__.'\Fixtures\InternalInterface { }');
|
||||
} elseif ('Test\\'.__NAMESPACE__.'\UseTraitWithInternalMethod' === $class) {
|
||||
} elseif ('Test\\'.UseTraitWithInternalMethod::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class UseTraitWithInternalMethod { use \\'.__NAMESPACE__.'\Fixtures\TraitWithInternalMethod; }');
|
||||
} elseif ('Test\\'.ExtendsVirtual::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtual extends ExtendsVirtualParent implements \\'.__NAMESPACE__.'\Fixtures\VirtualSubInterface {
|
||||
public function ownClassMethod() { }
|
||||
public function classMethod() { }
|
||||
public function sameLineInterfaceMethodNoBraces() { }
|
||||
}');
|
||||
} elseif ('Test\\'.ExtendsVirtualParent::class === $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\\'.ExtendsVirtualAbstract::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstract extends ExtendsVirtualAbstractBase {
|
||||
public static function staticMethod() { }
|
||||
public function ownAbstractMethod() { }
|
||||
public function interfaceMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.ExtendsVirtualAbstractBase::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; abstract class ExtendsVirtualAbstractBase extends \\'.__NAMESPACE__.'\Fixtures\VirtualClass implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
|
||||
public function ownAbstractBaseMethod() { }
|
||||
}');
|
||||
} elseif ('Test\\'.ExtendsVirtualMagicCall::class === $class) {
|
||||
eval('namespace Test\\'.__NAMESPACE__.'; class ExtendsVirtualMagicCall extends \\'.__NAMESPACE__.'\Fixtures\VirtualClassMagicCall implements \\'.__NAMESPACE__.'\Fixtures\VirtualInterface {
|
||||
}');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,12 +12,12 @@
|
||||
namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Psr\Log\LoggerInterface;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -70,8 +70,8 @@ class ErrorHandlerTest extends TestCase
|
||||
|
||||
public function testErrorGetLast()
|
||||
{
|
||||
$handler = ErrorHandler::register();
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->setDefaultLogger($logger);
|
||||
$handler->screamAt(E_ALL);
|
||||
|
||||
@@ -143,9 +143,8 @@ class ErrorHandlerTest extends TestCase
|
||||
public function testDefaultLogger()
|
||||
{
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
$handler = ErrorHandler::register();
|
||||
|
||||
$handler->setDefaultLogger($logger, E_NOTICE);
|
||||
$handler->setDefaultLogger($logger, [E_USER_NOTICE => LogLevel::CRITICAL]);
|
||||
@@ -234,7 +233,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($warnArgCheck))
|
||||
->willReturnCallback($warnArgCheck)
|
||||
;
|
||||
|
||||
$handler = ErrorHandler::register();
|
||||
@@ -262,7 +261,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
->willReturnCallback($logArgCheck)
|
||||
;
|
||||
|
||||
$handler = ErrorHandler::register();
|
||||
@@ -284,6 +283,10 @@ class ErrorHandlerTest extends TestCase
|
||||
|
||||
public function testHandleUserError()
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$this->markTestSkipped('PHP 7.4 allows __toString to throw exceptions');
|
||||
}
|
||||
|
||||
try {
|
||||
$handler = ErrorHandler::register();
|
||||
$handler->throwAt(0, true);
|
||||
@@ -318,7 +321,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
->willReturnCallback($logArgCheck)
|
||||
;
|
||||
|
||||
$handler = new ErrorHandler();
|
||||
@@ -331,12 +334,11 @@ class ErrorHandlerTest extends TestCase
|
||||
public function testHandleException()
|
||||
{
|
||||
try {
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
$handler = ErrorHandler::register();
|
||||
|
||||
$exception = new \Exception('foo');
|
||||
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
|
||||
$logArgCheck = function ($level, $message, $context) {
|
||||
$this->assertSame('Uncaught Exception: foo', $message);
|
||||
$this->assertArrayHasKey('exception', $context);
|
||||
@@ -346,7 +348,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logger
|
||||
->expects($this->exactly(2))
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
->willReturnCallback($logArgCheck)
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, E_ERROR);
|
||||
@@ -442,6 +444,7 @@ class ErrorHandlerTest extends TestCase
|
||||
public function testHandleFatalError()
|
||||
{
|
||||
try {
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
$handler = ErrorHandler::register();
|
||||
|
||||
$error = [
|
||||
@@ -451,8 +454,6 @@ class ErrorHandlerTest extends TestCase
|
||||
'line' => 123,
|
||||
];
|
||||
|
||||
$logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
|
||||
|
||||
$logArgCheck = function ($level, $message, $context) {
|
||||
$this->assertEquals('Fatal Parse Error: foo', $message);
|
||||
$this->assertArrayHasKey('exception', $context);
|
||||
@@ -462,7 +463,7 @@ class ErrorHandlerTest extends TestCase
|
||||
$logger
|
||||
->expects($this->once())
|
||||
->method('log')
|
||||
->will($this->returnCallback($logArgCheck))
|
||||
->willReturnCallback($logArgCheck)
|
||||
;
|
||||
|
||||
$handler->setDefaultLogger($logger, E_PARSE);
|
||||
@@ -481,7 +482,7 @@ class ErrorHandlerTest extends TestCase
|
||||
|
||||
public function testHandleErrorException()
|
||||
{
|
||||
$exception = new \Error("Class 'Foo' not found");
|
||||
$exception = new \Error("Class 'IReallyReallyDoNotExistAnywhereInTheRepositoryISwear' not found");
|
||||
|
||||
$handler = new ErrorHandler();
|
||||
$handler->setExceptionHandler(function () use (&$args) {
|
||||
@@ -491,14 +492,12 @@ class ErrorHandlerTest extends TestCase
|
||||
$handler->handleException($exception);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $args[0]);
|
||||
$this->assertStringStartsWith("Attempted to load class \"Foo\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
|
||||
$this->assertStringStartsWith("Attempted to load class \"IReallyReallyDoNotExistAnywhereInTheRepositoryISwear\" from the global namespace.\nDid you forget a \"use\" statement", $args[0]->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Exception
|
||||
*/
|
||||
public function testCustomExceptionHandler()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$handler = new ErrorHandler();
|
||||
$handler->setExceptionHandler(function ($e) use ($handler) {
|
||||
$handler->handleException($e);
|
||||
@@ -508,26 +507,40 @@ class ErrorHandlerTest extends TestCase
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider errorHandlerIsNotLostWhenLoggingProvider
|
||||
* @dataProvider errorHandlerWhenLoggingProvider
|
||||
*/
|
||||
public function testErrorHandlerIsNotLostWhenLogging($customErrorHandlerHasBeenPreviouslyDefined, LoggerInterface $logger)
|
||||
public function testErrorHandlerWhenLogging($previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined)
|
||||
{
|
||||
try {
|
||||
if ($customErrorHandlerHasBeenPreviouslyDefined) {
|
||||
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 ($customErrorHandlerHasBeenPreviouslyDefined) {
|
||||
if ($previousHandlerWasDefined) {
|
||||
restore_error_handler();
|
||||
}
|
||||
|
||||
if ($nextHandlerIsDefined) {
|
||||
restore_error_handler();
|
||||
}
|
||||
} finally {
|
||||
@@ -536,13 +549,14 @@ class ErrorHandlerTest extends TestCase
|
||||
}
|
||||
}
|
||||
|
||||
public function errorHandlerIsNotLostWhenLoggingProvider()
|
||||
public function errorHandlerWhenLoggingProvider()
|
||||
{
|
||||
return [
|
||||
[false, new NullLogger()],
|
||||
[true, new NullLogger()],
|
||||
[false, new LoggerThatSetAnErrorHandler()],
|
||||
[true, new LoggerThatSetAnErrorHandler()],
|
||||
];
|
||||
foreach ([false, true] as $previousHandlerWasDefined) {
|
||||
foreach ([false, true] as $loggerSetsAnotherHandler) {
|
||||
foreach ([false, true] as $nextHandlerIsDefined) {
|
||||
yield [$previousHandlerWasDefined, $loggerSetsAnotherHandler, $nextHandlerIsDefined];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,6 +237,10 @@ class FlattenExceptionTest extends TestCase
|
||||
|
||||
public function testArguments()
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
|
||||
}
|
||||
|
||||
$dh = opendir(__DIR__);
|
||||
$fh = tmpfile();
|
||||
|
||||
@@ -294,22 +298,30 @@ class FlattenExceptionTest extends TestCase
|
||||
|
||||
// assertEquals() does not like NAN values.
|
||||
$this->assertEquals($array[$i][0], 'float');
|
||||
$this->assertTrue(is_nan($array[$i++][1]));
|
||||
$this->assertNan($array[$i][1]);
|
||||
}
|
||||
|
||||
public function testRecursionInArguments()
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
|
||||
}
|
||||
|
||||
$a = null;
|
||||
$a = ['foo', [2, &$a]];
|
||||
$exception = $this->createException($a);
|
||||
|
||||
$flattened = FlattenException::create($exception);
|
||||
$trace = $flattened->getTrace();
|
||||
$this->assertContains('*DEEP NESTED ARRAY*', serialize($trace));
|
||||
$this->assertStringContainsString('*DEEP NESTED ARRAY*', serialize($trace));
|
||||
}
|
||||
|
||||
public function testTooBigArray()
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 70400) {
|
||||
$this->markTestSkipped('PHP 7.4 removes arguments from exception traces.');
|
||||
}
|
||||
|
||||
$a = [];
|
||||
for ($i = 0; $i < 20; ++$i) {
|
||||
for ($j = 0; $j < 50; ++$j) {
|
||||
@@ -329,8 +341,56 @@ class FlattenExceptionTest extends TestCase
|
||||
|
||||
$serializeTrace = serialize($trace);
|
||||
|
||||
$this->assertContains('*SKIPPED over 10000 entries*', $serializeTrace);
|
||||
$this->assertNotContains('*value1*', $serializeTrace);
|
||||
$this->assertStringContainsString('*SKIPPED over 10000 entries*', $serializeTrace);
|
||||
$this->assertStringNotContainsString('*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)
|
||||
|
||||
@@ -21,12 +21,12 @@ require_once __DIR__.'/HeaderMock.php';
|
||||
|
||||
class ExceptionHandlerTest extends TestCase
|
||||
{
|
||||
protected function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
testHeader();
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
testHeader();
|
||||
}
|
||||
@@ -39,8 +39,8 @@ class ExceptionHandlerTest extends TestCase
|
||||
$handler->sendPhpResponse(new \RuntimeException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertContains('Whoops, looks like something went wrong.', $response);
|
||||
$this->assertNotContains('<div class="trace trace-as-html">', $response);
|
||||
$this->assertStringContainsString('Whoops, looks like something went wrong.', $response);
|
||||
$this->assertStringNotContainsString('<div class="trace trace-as-html">', $response);
|
||||
|
||||
$handler = new ExceptionHandler(true);
|
||||
|
||||
@@ -48,8 +48,17 @@ class ExceptionHandlerTest extends TestCase
|
||||
$handler->sendPhpResponse(new \RuntimeException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertContains('Whoops, looks like something went wrong.', $response);
|
||||
$this->assertContains('<div class="trace trace-as-html">', $response);
|
||||
$this->assertStringContainsString('<h1 class="break-long-words exception-message">Foo</h1>', $response);
|
||||
$this->assertStringContainsString('<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="jAvascript: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->assertStringContainsString(sprintf('<h1 class="break-long-words exception-message">%s</h1>', htmlspecialchars($htmlWithXss, ENT_COMPAT | ENT_SUBSTITUTE, 'UTF-8')), $response);
|
||||
}
|
||||
|
||||
public function testStatusCode()
|
||||
@@ -60,7 +69,7 @@ class ExceptionHandlerTest extends TestCase
|
||||
$handler->sendPhpResponse(new NotFoundHttpException('Foo'));
|
||||
$response = ob_get_clean();
|
||||
|
||||
$this->assertContains('Sorry, the page you are looking for could not be found.', $response);
|
||||
$this->assertStringContainsString('Sorry, the page you are looking for could not be found.', $response);
|
||||
|
||||
$expectedHeaders = [
|
||||
['HTTP/1.0 404', true, null],
|
||||
@@ -76,7 +85,7 @@ class ExceptionHandlerTest extends TestCase
|
||||
|
||||
ob_start();
|
||||
$handler->sendPhpResponse(new MethodNotAllowedHttpException(['POST']));
|
||||
$response = ob_get_clean();
|
||||
ob_get_clean();
|
||||
|
||||
$expectedHeaders = [
|
||||
['HTTP/1.0 405', true, null],
|
||||
@@ -99,35 +108,65 @@ class ExceptionHandlerTest extends TestCase
|
||||
|
||||
public function testHandle()
|
||||
{
|
||||
$exception = new \Exception('foo');
|
||||
$handler = new ExceptionHandler(true);
|
||||
ob_start();
|
||||
|
||||
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
|
||||
$handler
|
||||
->expects($this->exactly(2))
|
||||
->method('sendPhpResponse');
|
||||
$handler->handle(new \Exception('foo'));
|
||||
|
||||
$handler->handle($exception);
|
||||
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'foo');
|
||||
}
|
||||
|
||||
$handler->setHandler(function ($e) use ($exception) {
|
||||
$this->assertSame($exception, $e);
|
||||
public function testHandleWithACustomHandlerThatOutputsSomething()
|
||||
{
|
||||
$handler = new ExceptionHandler(true);
|
||||
ob_start();
|
||||
$handler->setHandler(function () {
|
||||
echo 'ccc';
|
||||
});
|
||||
|
||||
$handler->handle($exception);
|
||||
$handler->handle(new \Exception());
|
||||
ob_end_flush(); // Necessary because of this PHP bug : https://bugs.php.net/76563
|
||||
$this->assertSame('ccc', ob_get_clean());
|
||||
}
|
||||
|
||||
public function testHandleWithACustomHandlerThatOutputsNothing()
|
||||
{
|
||||
$handler = new ExceptionHandler(true);
|
||||
$handler->setHandler(function () {});
|
||||
|
||||
$handler->handle(new \Exception('ccc'));
|
||||
|
||||
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
|
||||
}
|
||||
|
||||
public function testHandleWithACustomHandlerThatFails()
|
||||
{
|
||||
$handler = new ExceptionHandler(true);
|
||||
$handler->setHandler(function () {
|
||||
throw new \RuntimeException();
|
||||
});
|
||||
|
||||
$handler->handle(new \Exception('ccc'));
|
||||
|
||||
$this->assertThatTheExceptionWasOutput(ob_get_clean(), \Exception::class, 'Exception', 'ccc');
|
||||
}
|
||||
|
||||
public function testHandleOutOfMemoryException()
|
||||
{
|
||||
$exception = new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__);
|
||||
|
||||
$handler = $this->getMockBuilder('Symfony\Component\Debug\ExceptionHandler')->setMethods(['sendPhpResponse'])->getMock();
|
||||
$handler
|
||||
->expects($this->once())
|
||||
->method('sendPhpResponse');
|
||||
|
||||
$handler->setHandler(function ($e) {
|
||||
$handler = new ExceptionHandler(true);
|
||||
ob_start();
|
||||
$handler->setHandler(function () {
|
||||
$this->fail('OutOfMemoryException should bypass the handler');
|
||||
});
|
||||
|
||||
$handler->handle($exception);
|
||||
$handler->handle(new OutOfMemoryException('foo', 0, E_ERROR, __FILE__, __LINE__));
|
||||
|
||||
$this->assertThatTheExceptionWasOutput(ob_get_clean(), OutOfMemoryException::class, 'OutOfMemoryException', 'foo');
|
||||
}
|
||||
|
||||
private function assertThatTheExceptionWasOutput($content, $expectedClass, $expectedTitle, $expectedMessage)
|
||||
{
|
||||
$this->assertStringContainsString(sprintf('<span class="exception_title"><abbr title="%s">%s</abbr></span>', $expectedClass, $expectedTitle), $content);
|
||||
$this->assertStringContainsString(sprintf('<p class="break-long-words trace-message">%s</p>', $expectedMessage), $content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ use Symfony\Component\Debug\FatalErrorHandler\ClassNotFoundFatalErrorHandler;
|
||||
|
||||
class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
{
|
||||
public static function setUpBeforeClass()
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
foreach (spl_autoload_functions() as $function) {
|
||||
if (!\is_array($function)) {
|
||||
@@ -29,10 +29,14 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
// get class loaders wrapped by DebugClassLoader
|
||||
if ($function[0] instanceof DebugClassLoader) {
|
||||
$function = $function[0]->getClassLoader();
|
||||
|
||||
if (!\is_array($function)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($function[0] instanceof ComposerClassLoader) {
|
||||
$function[0]->add('Symfony_Component_Debug_Tests_Fixtures', \dirname(\dirname(\dirname(\dirname(\dirname(__DIR__))))));
|
||||
$function[0]->add('Symfony_Component_Debug_Tests_Fixtures', \dirname(__DIR__, 5));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -61,7 +65,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
}
|
||||
|
||||
$this->assertInstanceOf('Symfony\Component\Debug\Exception\ClassNotFoundException', $exception);
|
||||
$this->assertSame($translatedMessage, $exception->getMessage());
|
||||
$this->assertRegExp($translatedMessage, $exception->getMessage());
|
||||
$this->assertSame($error['type'], $exception->getSeverity());
|
||||
$this->assertSame($error['file'], $exception->getFile());
|
||||
$this->assertSame($error['line'], $exception->getLine());
|
||||
@@ -71,6 +75,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
{
|
||||
$autoloader = new ComposerClassLoader();
|
||||
$autoloader->add('Symfony\Component\Debug\Exception\\', realpath(__DIR__.'/../../Exception'));
|
||||
$autoloader->add('Symfony_Component_Debug_Tests_Fixtures', realpath(__DIR__.'/../../Tests/Fixtures'));
|
||||
|
||||
$debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']);
|
||||
|
||||
@@ -82,7 +87,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
'file' => 'foo.php',
|
||||
'message' => 'Class \'WhizBangFactory\' not found',
|
||||
],
|
||||
"Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement?",
|
||||
"/^Attempted to load class \"WhizBangFactory\" from the global namespace.\nDid you forget a \"use\" statement\?$/",
|
||||
],
|
||||
[
|
||||
[
|
||||
@@ -91,7 +96,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
'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?",
|
||||
"/^Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/",
|
||||
],
|
||||
[
|
||||
[
|
||||
@@ -100,7 +105,8 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
'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\"?",
|
||||
"/^Attempted to load class \"UndefinedFunctionException\" from the global namespace.\nDid you forget a \"use\" statement for .*\"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?$/",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
[
|
||||
@@ -109,35 +115,7 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
'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\"?",
|
||||
],
|
||||
[
|
||||
[
|
||||
'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\"?",
|
||||
],
|
||||
[
|
||||
[
|
||||
'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'],
|
||||
],
|
||||
[
|
||||
[
|
||||
'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\"?",
|
||||
"/^Attempted to load class \"PEARClass\" from the global namespace.\nDid you forget a \"use\" statement for \"Symfony_Component_Debug_Tests_Fixtures_PEARClass\"\?$/",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
@@ -147,7 +125,37 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase
|
||||
'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?",
|
||||
"/^Attempted to load class \"UndefinedFunctionException\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for .*\"Symfony\\\\Component\\\\Debug\\\\Exception\\\\UndefinedFunctionException\"\?$/",
|
||||
[$debugClassLoader, 'loadClass'],
|
||||
],
|
||||
[
|
||||
[
|
||||
'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'],
|
||||
],
|
||||
[
|
||||
[
|
||||
'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'],
|
||||
],
|
||||
[
|
||||
[
|
||||
'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 */ },
|
||||
],
|
||||
];
|
||||
|
||||
34
Tests/Fixtures/ClassWithAnnotatedParameters.php
Normal file
34
Tests/Fixtures/ClassWithAnnotatedParameters.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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()
|
||||
{
|
||||
}
|
||||
}
|
||||
11
Tests/Fixtures/DefinitionInEvaluatedCode.php
Normal file
11
Tests/Fixtures/DefinitionInEvaluatedCode.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
eval('
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
class DefinitionInEvaluatedCode
|
||||
{
|
||||
}
|
||||
');
|
||||
22
Tests/Fixtures/ErrorHandlerThatUsesThePreviousOne.php
Normal file
22
Tests/Fixtures/ErrorHandlerThatUsesThePreviousOne.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ class FinalClass3
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @author John Doe
|
||||
*/
|
||||
class FinalClass4
|
||||
@@ -63,7 +64,6 @@ class FinalClass6
|
||||
* @author John Doe
|
||||
*
|
||||
* @final another
|
||||
*
|
||||
* multiline comment...
|
||||
*
|
||||
* @return string
|
||||
@@ -76,6 +76,7 @@ class FinalClass7
|
||||
/**
|
||||
* @author John Doe
|
||||
* @final
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
class FinalClass8
|
||||
|
||||
@@ -13,6 +13,8 @@ class FinalMethod
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function finalMethod2()
|
||||
{
|
||||
|
||||
27
Tests/Fixtures/InterfaceWithAnnotatedParameters.php
Normal file
27
Tests/Fixtures/InterfaceWithAnnotatedParameters.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?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();
|
||||
}
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
use Psr\Log\AbstractLogger;
|
||||
use Symfony\Component\Debug\BufferingLogger;
|
||||
|
||||
class LoggerThatSetAnErrorHandler extends AbstractLogger
|
||||
class LoggerThatSetAnErrorHandler extends BufferingLogger
|
||||
{
|
||||
public function log($level, $message, array $context = [])
|
||||
{
|
||||
set_error_handler('is_string');
|
||||
parent::log($level, $message, $context);
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
||||
32
Tests/Fixtures/SubClassWithAnnotatedParameters.php
Normal file
32
Tests/Fixtures/SubClassWithAnnotatedParameters.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?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()
|
||||
{
|
||||
}
|
||||
}
|
||||
13
Tests/Fixtures/TraitWithAnnotatedParameters.php
Normal file
13
Tests/Fixtures/TraitWithAnnotatedParameters.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
trait TraitWithAnnotatedParameters
|
||||
{
|
||||
/**
|
||||
* `@param` annotations in traits are not parsed.
|
||||
*/
|
||||
public function isSymfony()
|
||||
{
|
||||
}
|
||||
}
|
||||
11
Tests/Fixtures/VirtualClass.php
Normal file
11
Tests/Fixtures/VirtualClass.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @method string classMethod()
|
||||
*/
|
||||
class VirtualClass
|
||||
{
|
||||
use VirtualTrait;
|
||||
}
|
||||
18
Tests/Fixtures/VirtualClassMagicCall.php
Normal file
18
Tests/Fixtures/VirtualClassMagicCall.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?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)
|
||||
{
|
||||
}
|
||||
}
|
||||
34
Tests/Fixtures/VirtualInterface.php
Normal file
34
Tests/Fixtures/VirtualInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
10
Tests/Fixtures/VirtualSubInterface.php
Normal file
10
Tests/Fixtures/VirtualSubInterface.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @method string subInterfaceMethod()
|
||||
*/
|
||||
interface VirtualSubInterface extends VirtualInterface
|
||||
{
|
||||
}
|
||||
10
Tests/Fixtures/VirtualTrait.php
Normal file
10
Tests/Fixtures/VirtualTrait.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Debug\Tests\Fixtures;
|
||||
|
||||
/**
|
||||
* @method string traitMethod()
|
||||
*/
|
||||
trait VirtualTrait
|
||||
{
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Debug\Tests;
|
||||
|
||||
use Symfony\Component\Debug\ExceptionHandler;
|
||||
|
||||
class MockExceptionHandler extends ExceptionHandler
|
||||
{
|
||||
public $e;
|
||||
|
||||
public function handle(\Exception $e)
|
||||
{
|
||||
$this->e = $e;
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ var_dump([
|
||||
$eHandler[0]->setExceptionHandler('print_r');
|
||||
|
||||
if (true) {
|
||||
class Broken implements \Serializable
|
||||
class Broken implements \JsonSerializable
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,6 @@ array(1) {
|
||||
}
|
||||
object(Symfony\Component\Debug\Exception\FatalErrorException)#%d (%d) {
|
||||
["message":protected]=>
|
||||
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)"
|
||||
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)"
|
||||
%a
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"minimum-stability": "dev",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "4.1-dev"
|
||||
"dev-master": "4.3-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user