From 275496a1f429fa845880d090c18ad613ae6ae072 Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Thu, 3 Sep 2020 23:57:37 +0200 Subject: [PATCH] [Debug] Parse "x not found" errors correctly on php 8. --- .../ClassNotFoundFatalErrorHandler.php | 54 +++++++------------ .../ClassNotFoundFatalErrorHandlerTest.php | 36 +++++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php index 9bae6f8656..b1216fe7ae 100644 --- a/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php +++ b/src/Symfony/Component/Debug/FatalErrorHandler/ClassNotFoundFatalErrorHandler.php @@ -29,50 +29,34 @@ class ClassNotFoundFatalErrorHandler implements FatalErrorHandlerInterface */ public function handleError(array $error, FatalErrorException $exception) { - $messageLen = \strlen($error['message']); - $notFoundSuffix = '\' not found'; - $notFoundSuffixLen = \strlen($notFoundSuffix); - if ($notFoundSuffixLen > $messageLen) { + if (!preg_match('/^(Class|Interface|Trait) [\'"]([^\'"]+)[\'"] not found$/', $error['message'], $matches)) { return null; } + $typeName = strtolower($matches[1]); + $fullyQualifiedClassName = $matches[2]; - if (0 !== substr_compare($error['message'], $notFoundSuffix, -$notFoundSuffixLen)) { - return null; + if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { + $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); + $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); + $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); + $tail = ' for another namespace?'; + } else { + $className = $fullyQualifiedClassName; + $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); + $tail = '?'; } - foreach (['class', 'interface', 'trait'] as $typeName) { - $prefix = ucfirst($typeName).' \''; - $prefixLen = \strlen($prefix); - if (0 !== strpos($error['message'], $prefix)) { - continue; - } - - $fullyQualifiedClassName = substr($error['message'], $prefixLen, -$notFoundSuffixLen); - if (false !== $namespaceSeparatorIndex = strrpos($fullyQualifiedClassName, '\\')) { - $className = substr($fullyQualifiedClassName, $namespaceSeparatorIndex + 1); - $namespacePrefix = substr($fullyQualifiedClassName, 0, $namespaceSeparatorIndex); - $message = sprintf('Attempted to load %s "%s" from namespace "%s".', $typeName, $className, $namespacePrefix); - $tail = ' for another namespace?'; + if ($candidates = $this->getClassCandidates($className)) { + $tail = array_pop($candidates).'"?'; + if ($candidates) { + $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; } else { - $className = $fullyQualifiedClassName; - $message = sprintf('Attempted to load %s "%s" from the global namespace.', $typeName, $className); - $tail = '?'; + $tail = ' for "'.$tail; } - - if ($candidates = $this->getClassCandidates($className)) { - $tail = array_pop($candidates).'"?'; - if ($candidates) { - $tail = ' for e.g. "'.implode('", "', $candidates).'" or "'.$tail; - } else { - $tail = ' for "'.$tail; - } - } - $message .= "\nDid you forget a \"use\" statement".$tail; - - return new ClassNotFoundException($message, $exception); } + $message .= "\nDid you forget a \"use\" statement".$tail; - return null; + return new ClassNotFoundException($message, $exception); } /** diff --git a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php index d408cb43db..2b8a867882 100644 --- a/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php +++ b/src/Symfony/Component/Debug/Tests/FatalErrorHandler/ClassNotFoundFatalErrorHandlerTest.php @@ -80,6 +80,15 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase $debugClassLoader = new DebugClassLoader([$autoloader, 'loadClass']); return [ + [ + [ + '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\?$/", + ], [ [ 'type' => 1, @@ -98,6 +107,33 @@ class ClassNotFoundFatalErrorHandlerTest extends TestCase ], "/^Attempted to load class \"WhizBangFactory\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/", ], + [ + [ + '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\?$/", + ], + [ + [ + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Interface "Foo\\Bar\\WhizBangInterface" not found', + ], + "/^Attempted to load interface \"WhizBangInterface\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/", + ], + [ + [ + 'type' => 1, + 'line' => 12, + 'file' => 'foo.php', + 'message' => 'Trait "Foo\\Bar\\WhizBangTrait" not found', + ], + "/^Attempted to load trait \"WhizBangTrait\" from namespace \"Foo\\\\Bar\".\nDid you forget a \"use\" statement for another namespace\?$/", + ], [ [ 'type' => 1,