FIX Fix namespace parsing under PHP 8, tweak readability of parser

$hadNamespace was ambiguously named, so the original PHP 8 support
update marked it true when it was strictly meant to indicate that a
namespace separator token had been encountered, resulting in bungled
parsing of complex class specs like Class(["arg" => true]).
This commit is contained in:
Garion Herman 2020-09-30 13:05:15 +13:00
parent effe5c2e6f
commit 8ad4c4e024
2 changed files with 44 additions and 7 deletions

View File

@ -430,7 +430,7 @@ class ClassInfo
// Keep track of the current bucket that we're putting data into
$bucket = &$args;
$bucketStack = [];
$hadNamespace = false;
$lastTokenWasNSSeparator = false;
$currentKey = null;
foreach ($tokens as $token) {
@ -443,20 +443,20 @@ class ClassInfo
($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED)
) {
// PHP 8 exposes the FQCN as a single T_NAME_QUALIFIED or T_NAME_FULLY_QUALIFIED token
$class .= $token[1];
$hadNamespace = true;
$class = $token[1];
} elseif ($class === null && is_array($token) && $token[0] === T_STRING) {
$class = $token[1];
} elseif (is_array($token) && $token[0] === T_NS_SEPARATOR) {
$class .= $token[1];
$hadNamespace = true;
$lastTokenWasNSSeparator = true;
} elseif ($token === '.') {
// Treat service name separator as NS separator
$class .= '.';
$hadNamespace = true;
} elseif ($hadNamespace && is_array($token) && $token[0] === T_STRING) {
$lastTokenWasNSSeparator = true;
} elseif ($lastTokenWasNSSeparator && is_array($token) && $token[0] === T_STRING) {
// Found another section of a namespaced class
$class .= $token[1];
$hadNamespace = false;
$lastTokenWasNSSeparator = false;
// Get arguments
} elseif (is_array($token)) {
switch ($token[0]) {

View File

@ -265,4 +265,41 @@ class ClassInfoTest extends SapphireTest
'ClassInfo::testClassesWithExtension() returns no classes after an extension being removed'
);
}
/**
* @dataProvider provideClassSpecCases
*/
public function testParseClassSpec($input, $output)
{
$this->assertEquals(
$output,
ClassInfo::parse_class_spec($input)
);
}
public function provideClassSpecCases()
{
return [
'Standard class' => [
'SimpleClass',
['SimpleClass', []],
],
'Namespaced class' => [
'Foo\\Bar\\NamespacedClass',
['Foo\\Bar\\NamespacedClass', []],
],
'Namespaced class with service name' => [
'Foo\\Bar\\NamespacedClass.withservicename',
['Foo\\Bar\\NamespacedClass.withservicename', []],
],
'Namespaced class with argument' => [
'Foo\\Bar\\NamespacedClass(["with-arg" => true])',
['Foo\\Bar\\NamespacedClass', [["with-arg" => true]]],
],
'Namespaced class with service name and argument' => [
'Foo\\Bar\\NamespacedClass.withmodifier(["and-arg" => true])',
['Foo\\Bar\\NamespacedClass.withmodifier', [["and-arg" => true]]],
],
];
}
}