From ba6332388cd8ae57103fb9c21295e57b52cea022 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 30 Aug 2020 19:22:21 +0200 Subject: [PATCH] PHP 8.0: "undo" namespaced names as single token As per the proposal in 3041. This effectively "undoes" the new PHP 8.0 tokenization of identifier names for PHPCS 3.x. Includes extensive unit tests to ensure the correct re-tokenization as well as that the rest of the tokenization is not adversely affected by this change. Includes preventing `function ...` within a group use statement from breaking the retokenization. Includes fixing the nullable tokenization when combined with any of the new PHP 8 identifier name tokens. --- package.xml | 6 + src/Tokenizers/PHP.php | 109 +- src/Util/Tokens.php | 12 + .../UndoNamespacedNameSingleTokenTest.inc | 147 ++ .../UndoNamespacedNameSingleTokenTest.php | 1297 +++++++++++++++++ 5 files changed, 1562 insertions(+), 9 deletions(-) create mode 100644 tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc create mode 100644 tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.php diff --git a/package.xml b/package.xml index 3c6eb4b784..8d3b63fb50 100644 --- a/package.xml +++ b/package.xml @@ -121,6 +121,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -1997,6 +1999,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + @@ -2058,6 +2062,8 @@ http://pear.php.net/dtd/package-2.0.xsd"> + + diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 5180a5f0e8..d76e0d60ac 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -815,6 +815,81 @@ protected function tokenize($string) continue; }//end if + /* + As of PHP 8.0 fully qualified, partially qualified and namespace relative + identifier names are tokenized differently. + This "undoes" the new tokenization so the tokenization will be the same in + in PHP 5, 7 and 8. + */ + + if (PHP_VERSION_ID >= 80000 + && $tokenIsArray === true + && ($token[0] === T_NAME_QUALIFIED + || $token[0] === T_NAME_FULLY_QUALIFIED + || $token[0] === T_NAME_RELATIVE) + ) { + $name = $token[1]; + + if ($token[0] === T_NAME_FULLY_QUALIFIED) { + $newToken = []; + $newToken['code'] = T_NS_SEPARATOR; + $newToken['type'] = 'T_NS_SEPARATOR'; + $newToken['content'] = '\\'; + $finalTokens[$newStackPtr] = $newToken; + ++$newStackPtr; + + $name = ltrim($name, '\\'); + } + + if ($token[0] === T_NAME_RELATIVE) { + $newToken = []; + $newToken['code'] = T_NAMESPACE; + $newToken['type'] = 'T_NAMESPACE'; + $newToken['content'] = substr($name, 0, 9); + $finalTokens[$newStackPtr] = $newToken; + ++$newStackPtr; + + $newToken = []; + $newToken['code'] = T_NS_SEPARATOR; + $newToken['type'] = 'T_NS_SEPARATOR'; + $newToken['content'] = '\\'; + $finalTokens[$newStackPtr] = $newToken; + ++$newStackPtr; + + $name = substr($name, 10); + } + + $parts = explode('\\', $name); + $partCount = count($parts); + $lastPart = ($partCount - 1); + + foreach ($parts as $i => $part) { + $newToken = []; + $newToken['code'] = T_STRING; + $newToken['type'] = 'T_STRING'; + $newToken['content'] = $part; + $finalTokens[$newStackPtr] = $newToken; + ++$newStackPtr; + + if ($i !== $lastPart) { + $newToken = []; + $newToken['code'] = T_NS_SEPARATOR; + $newToken['type'] = 'T_NS_SEPARATOR'; + $newToken['content'] = '\\'; + $finalTokens[$newStackPtr] = $newToken; + ++$newStackPtr; + } + } + + if (PHP_CODESNIFFER_VERBOSITY > 1) { + $type = Util\Tokens::tokenName($token[0]); + $content = Util\Common::prepareForOutput($token[1]); + echo "\t\t* token $stackPtr split into individual tokens; was: $type => $content".PHP_EOL; + } + + continue; + }//end if + /* Before PHP 7.0, the "yield from" was tokenized as T_YIELD, T_WHITESPACE and T_STRING. So look for @@ -1131,7 +1206,7 @@ protected function tokenize($string) * Check if the next non-empty token is one of the tokens which can be used * in type declarations. If not, it's definitely a ternary. * At this point, the only token types which need to be taken into consideration - * as potential type declarations are T_STRING, T_ARRAY, T_CALLABLE and T_NS_SEPARATOR. + * as potential type declarations are identifier names, T_ARRAY, T_CALLABLE and T_NS_SEPARATOR. */ $lastRelevantNonEmpty = null; @@ -1148,6 +1223,9 @@ protected function tokenize($string) } if ($tokenType === T_STRING + || $tokenType === T_NAME_FULLY_QUALIFIED + || $tokenType === T_NAME_RELATIVE + || $tokenType === T_NAME_QUALIFIED || $tokenType === T_ARRAY || $tokenType === T_NS_SEPARATOR ) { @@ -1159,7 +1237,10 @@ protected function tokenize($string) && isset($lastRelevantNonEmpty) === false) || ($lastRelevantNonEmpty === T_ARRAY && $tokenType === '(') - || ($lastRelevantNonEmpty === T_STRING + || (($lastRelevantNonEmpty === T_STRING + || $lastRelevantNonEmpty === T_NAME_FULLY_QUALIFIED + || $lastRelevantNonEmpty === T_NAME_RELATIVE + || $lastRelevantNonEmpty === T_NAME_QUALIFIED) && ($tokenType === T_DOUBLE_COLON || $tokenType === '(' || $tokenType === ':')) @@ -1304,6 +1385,10 @@ protected function tokenize($string) tokenized as T_STRING even if it appears to be a different token, such as when writing code like: function default(): foo so go forward and change the token type before it is processed. + + Note: this should not be done for `function Level\Name` within a + group use statement for the PHP 8 identifier name tokens as it + would interfere with the re-tokenization of those. */ if ($tokenIsArray === true @@ -1321,7 +1406,10 @@ protected function tokenize($string) } } - if ($x < $numTokens && is_array($tokens[$x]) === true) { + if ($x < $numTokens + && is_array($tokens[$x]) === true + && $tokens[$x][0] !== T_NAME_QUALIFIED + ) { if (PHP_CODESNIFFER_VERBOSITY > 1) { $oldType = Util\Tokens::tokenName($tokens[$x][0]); echo "\t\t* token $x changed from $oldType to T_STRING".PHP_EOL; @@ -1377,12 +1465,15 @@ function return types. We want to keep the parenthesis map clean, && $tokens[$x] === ':' ) { $allowed = [ - T_STRING => T_STRING, - T_ARRAY => T_ARRAY, - T_CALLABLE => T_CALLABLE, - T_SELF => T_SELF, - T_PARENT => T_PARENT, - T_NS_SEPARATOR => T_NS_SEPARATOR, + T_STRING => T_STRING, + T_NAME_FULLY_QUALIFIED => T_NAME_FULLY_QUALIFIED, + T_NAME_RELATIVE => T_NAME_RELATIVE, + T_NAME_QUALIFIED => T_NAME_QUALIFIED, + T_ARRAY => T_ARRAY, + T_CALLABLE => T_CALLABLE, + T_SELF => T_SELF, + T_PARENT => T_PARENT, + T_NS_SEPARATOR => T_NS_SEPARATOR, ]; $allowed += Util\Tokens::$emptyTokens; diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php index 3994264067..952ccc2c12 100644 --- a/src/Util/Tokens.php +++ b/src/Util/Tokens.php @@ -129,6 +129,18 @@ define('T_NULLSAFE_OBJECT_OPERATOR', 'PHPCS_T_NULLSAFE_OBJECT_OPERATOR'); } +if (defined('T_NAME_QUALIFIED') === false) { + define('T_NAME_QUALIFIED', 'PHPCS_T_NAME_QUALIFIED'); +} + +if (defined('T_NAME_FULLY_QUALIFIED') === false) { + define('T_NAME_FULLY_QUALIFIED', 'PHPCS_T_NAME_FULLY_QUALIFIED'); +} + +if (defined('T_NAME_RELATIVE') === false) { + define('T_NAME_RELATIVE', 'PHPCS_T_NAME_RELATIVE'); +} + // Tokens used for parsing doc blocks. define('T_DOC_COMMENT_STAR', 'PHPCS_T_DOC_COMMENT_STAR'); define('T_DOC_COMMENT_WHITESPACE', 'PHPCS_T_DOC_COMMENT_WHITESPACE'); diff --git a/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc b/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc new file mode 100644 index 0000000000..540f72c9dc --- /dev/null +++ b/tests/Core/Tokenizer/UndoNamespacedNameSingleTokenTest.inc @@ -0,0 +1,147 @@ + + * @copyright 2020 Squiz Pty Ltd (ABN 77 084 670 600) + * @license https://github.com/squizlabs/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Tests\Core\Tokenizer; + +use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest; + +class UndoNamespacedNameSingleTokenTest extends AbstractMethodUnitTest +{ + + + /** + * Test that identifier names are tokenized the same across PHP versions, based on the PHP 5/7 tokenization. + * + * @param string $testMarker The comment prefacing the test. + * @param array $expectedTokens The tokenization expected. + * + * @dataProvider dataIdentifierTokenization + * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize + * + * @return void + */ + public function testIdentifierTokenization($testMarker, $expectedTokens) + { + $tokens = self::$phpcsFile->getTokens(); + $identifier = $this->getTargetToken($testMarker, constant($expectedTokens[0]['type'])); + + foreach ($expectedTokens as $key => $tokenInfo) { + $this->assertSame(constant($tokenInfo['type']), $tokens[$identifier]['code']); + $this->assertSame($tokenInfo['type'], $tokens[$identifier]['type']); + $this->assertSame($tokenInfo['content'], $tokens[$identifier]['content']); + + ++$identifier; + } + + }//end testIdentifierTokenization() + + + /** + * Data provider. + * + * @see testIdentifierTokenization() + * + * @return array + */ + public function dataIdentifierTokenization() + { + return [ + [ + '/* testNamespaceDeclaration */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Package', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testNamespaceDeclarationWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Domain', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Domain', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testFunctionUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testFunctionUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_in_ns', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testConstantUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_NAME', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testConstantUseStatementWithLevels */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'OTHER_CONSTANT', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testMultiUseUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'UnqualifiedClassName', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testMultiUsePartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'PartiallyClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testGroupUseStatement */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_OPEN_USE_GROUP', + 'content' => '{', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'AnotherDomain', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_grouped', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_GROUPED', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sub', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'YetAnotherDomain', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevelA', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_grouped_too', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'const', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevelB', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'CONSTANT_GROUPED_TOO', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_CLOSE_USE_GROUP', + 'content' => '}', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testClassName */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'MyClass', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testExtendedFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'FQN', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testImplementsRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Unqualified', + ], + [ + 'type' => 'T_COMMA', + 'content' => ',', + ], + ], + ], + [ + '/* testImplementsPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Sub', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + [ + '/* testFunctionName */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testTypeDeclarationRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + // TODO: change this to T_TYPE_UNION when #3032 is merged. + [ + 'type' => 'T_BITWISE_OR', + 'content' => '|', + ], + [ + 'type' => 'T_STRING', + 'content' => 'object', + ], + ], + ], + [ + '/* testTypeDeclarationFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testTypeDeclarationUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Unqualified', + ], + // TODO: change this to T_TYPE_UNION when #3032 is merged. + [ + 'type' => 'T_BITWISE_OR', + 'content' => '|', + ], + [ + 'type' => 'T_FALSE', + 'content' => 'false', + ], + ], + ], + [ + '/* testTypeDeclarationPartiallyQualified */', + [ + [ + 'type' => 'T_NULLABLE', + 'content' => '?', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testReturnTypeFQN */', + [ + [ + 'type' => 'T_NULLABLE', + 'content' => '?', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testFunctionCallRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'NameSpace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionCallFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Package', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionCallUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testFunctionPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testCatchRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'SubLevel', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testCatchPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Exception', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testNewRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testNewFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Vendor', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testNewUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testNewPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testDoubleColonRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testDoubleColonPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Level', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_DOUBLE_COLON', + 'content' => '::', + ], + ], + ], + [ + '/* testInstanceOfRelative */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testInstanceOfFQN */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Full', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_CLOSE_PARENTHESIS', + 'content' => ')', + ], + ], + ], + [ + '/* testInstanceOfUnqualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + ], + ], + [ + '/* testInstanceOfPartiallyQualified */', + [ + [ + 'type' => 'T_STRING', + 'content' => 'Partially', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'ClassName', + ], + [ + 'type' => 'T_SEMICOLON', + 'content' => ';', + ], + ], + ], + [ + '/* testInvalidInPHP8Whitespace */', + [ + [ + 'type' => 'T_NAMESPACE', + 'content' => 'namespace', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Sublevel', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_STRING', + 'content' => 'function_name', + ], + [ + 'type' => 'T_OPEN_PARENTHESIS', + 'content' => '(', + ], + ], + ], + [ + '/* testInvalidInPHP8Comments */', + [ + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Fully', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_PHPCS_IGNORE', + 'content' => '// phpcs:ignore Stnd.Cat.Sniff -- for reasons +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Qualified', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_COMMENT', + 'content' => '/* comment */', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' ', + ], + [ + 'type' => 'T_NS_SEPARATOR', + 'content' => '\\', + ], + [ + 'type' => 'T_STRING', + 'content' => 'Name', + ], + [ + 'type' => 'T_WHITESPACE', + 'content' => ' +', + ], + ], + ], + ]; + + }//end dataIdentifierTokenization() + + +}//end class