diff --git a/package.xml b/package.xml
index e6eca0b0e3..5fb7008143 100644
--- a/package.xml
+++ b/package.xml
@@ -172,6 +172,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2074,6 +2076,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
@@ -2148,6 +2152,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
+
+
diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php
index 81db29a59e..f1c363c002 100644
--- a/src/Tokenizers/PHP.php
+++ b/src/Tokenizers/PHP.php
@@ -893,6 +893,62 @@ protected function tokenize($string)
continue;
}//end if
+ /*
+ Tokenize the parameter labels for PHP 8.0 named parameters as a special T_PARAM_NAME
+ token and ensure that the colon after it is always T_COLON.
+ */
+
+ if ($tokenIsArray === true
+ && preg_match('`^[a-zA-Z_\x80-\xff]`', $token[1]) === 1
+ ) {
+ // Get the next non-empty token.
+ for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
+ if (is_array($tokens[$i]) === false
+ || isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === false
+ ) {
+ break;
+ }
+ }
+
+ if (isset($tokens[$i]) === true
+ && is_array($tokens[$i]) === false
+ && $tokens[$i] === ':'
+ ) {
+ // Get the previous non-empty token.
+ for ($j = ($stackPtr - 1); $j > 0; $j--) {
+ if (is_array($tokens[$j]) === false
+ || isset(Util\Tokens::$emptyTokens[$tokens[$j][0]]) === false
+ ) {
+ break;
+ }
+ }
+
+ if (is_array($tokens[$j]) === false
+ && ($tokens[$j] === '('
+ || $tokens[$j] === ',')
+ ) {
+ $newToken = [];
+ $newToken['code'] = T_PARAM_NAME;
+ $newToken['type'] = 'T_PARAM_NAME';
+ $newToken['content'] = $token[1];
+ $finalTokens[$newStackPtr] = $newToken;
+
+ $newStackPtr++;
+
+ // Modify the original token stack so that future checks, like
+ // determining T_COLON vs T_INLINE_ELSE can handle this correctly.
+ $tokens[$stackPtr][0] = T_PARAM_NAME;
+
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ $type = Util\Tokens::tokenName($token[0]);
+ echo "\t\t* token $stackPtr changed from $type to T_PARAM_NAME".PHP_EOL;
+ }
+
+ continue;
+ }
+ }//end if
+ }//end if
+
/*
Before PHP 7.0, the "yield from" was tokenized as
T_YIELD, T_WHITESPACE and T_STRING. So look for
@@ -1701,76 +1757,98 @@ function return types. We want to keep the parenthesis map clean,
// Convert colons that are actually the ELSE component of an
// inline IF statement.
if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
- // Make sure this isn't a return type separator.
$isInlineIf = true;
+
+ // Make sure this isn't a named parameter label.
+ // Get the previous non-empty token.
for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (is_array($tokens[$i]) === false
- || ($tokens[$i][0] !== T_DOC_COMMENT
- && $tokens[$i][0] !== T_COMMENT
- && $tokens[$i][0] !== T_WHITESPACE)
+ || isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === false
) {
break;
}
}
- if ($tokens[$i] === ')') {
- $parenCount = 1;
- for ($i--; $i > 0; $i--) {
- if ($tokens[$i] === '(') {
- $parenCount--;
- if ($parenCount === 0) {
- break;
- }
- } else if ($tokens[$i] === ')') {
- $parenCount++;
- }
+ if ($tokens[$i][0] === T_PARAM_NAME) {
+ $isInlineIf = false;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is parameter label, not T_INLINE_ELSE".PHP_EOL;
}
+ }
- // We've found the open parenthesis, so if the previous
- // non-empty token is FUNCTION or USE, this is a return type.
- // Note that we need to skip T_STRING tokens here as these
- // can be function names.
- for ($i--; $i > 0; $i--) {
+ if ($isInlineIf === true) {
+ // Make sure this isn't a return type separator.
+ for ($i = ($stackPtr - 1); $i > 0; $i--) {
if (is_array($tokens[$i]) === false
|| ($tokens[$i][0] !== T_DOC_COMMENT
&& $tokens[$i][0] !== T_COMMENT
- && $tokens[$i][0] !== T_WHITESPACE
- && $tokens[$i][0] !== T_STRING)
+ && $tokens[$i][0] !== T_WHITESPACE)
) {
break;
}
}
- if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_FN || $tokens[$i][0] === T_USE) {
- $isInlineIf = false;
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- echo "\t\t* token is return type, not T_INLINE_ELSE".PHP_EOL;
+ if ($tokens[$i] === ')') {
+ $parenCount = 1;
+ for ($i--; $i > 0; $i--) {
+ if ($tokens[$i] === '(') {
+ $parenCount--;
+ if ($parenCount === 0) {
+ break;
+ }
+ } else if ($tokens[$i] === ')') {
+ $parenCount++;
+ }
}
- }
+
+ // We've found the open parenthesis, so if the previous
+ // non-empty token is FUNCTION or USE, this is a return type.
+ // Note that we need to skip T_STRING tokens here as these
+ // can be function names.
+ for ($i--; $i > 0; $i--) {
+ if (is_array($tokens[$i]) === false
+ || ($tokens[$i][0] !== T_DOC_COMMENT
+ && $tokens[$i][0] !== T_COMMENT
+ && $tokens[$i][0] !== T_WHITESPACE
+ && $tokens[$i][0] !== T_STRING)
+ ) {
+ break;
+ }
+ }
+
+ if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_FN || $tokens[$i][0] === T_USE) {
+ $isInlineIf = false;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is return type, not T_INLINE_ELSE".PHP_EOL;
+ }
+ }
+ }//end if
}//end if
// Check to see if this is a CASE or DEFAULT opener.
- $inlineIfToken = $insideInlineIf[(count($insideInlineIf) - 1)];
- for ($i = $stackPtr; $i > $inlineIfToken; $i--) {
- if (is_array($tokens[$i]) === true
- && ($tokens[$i][0] === T_CASE
- || $tokens[$i][0] === T_DEFAULT)
- ) {
- $isInlineIf = false;
- if (PHP_CODESNIFFER_VERBOSITY > 1) {
- echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE".PHP_EOL;
- }
+ if ($isInlineIf === true) {
+ $inlineIfToken = $insideInlineIf[(count($insideInlineIf) - 1)];
+ for ($i = $stackPtr; $i > $inlineIfToken; $i--) {
+ if (is_array($tokens[$i]) === true
+ && ($tokens[$i][0] === T_CASE
+ || $tokens[$i][0] === T_DEFAULT)
+ ) {
+ $isInlineIf = false;
+ if (PHP_CODESNIFFER_VERBOSITY > 1) {
+ echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE".PHP_EOL;
+ }
- break;
- }
+ break;
+ }
- if (is_array($tokens[$i]) === false
- && ($tokens[$i] === ';'
- || $tokens[$i] === '{')
- ) {
- break;
+ if (is_array($tokens[$i]) === false
+ && ($tokens[$i] === ';'
+ || $tokens[$i] === '{')
+ ) {
+ break;
+ }
}
- }
+ }//end if
if ($isInlineIf === true) {
array_pop($insideInlineIf);
diff --git a/src/Util/Tokens.php b/src/Util/Tokens.php
index aee035d352..ac05362676 100644
--- a/src/Util/Tokens.php
+++ b/src/Util/Tokens.php
@@ -76,6 +76,7 @@
define('T_ZSR_EQUAL', 'PHPCS_T_ZSR_EQUAL');
define('T_FN_ARROW', 'T_FN_ARROW');
define('T_TYPE_UNION', 'T_TYPE_UNION');
+define('T_PARAM_NAME', 'T_PARAM_NAME');
// Some PHP 5.5 tokens, replicated for lower versions.
if (defined('T_FINALLY') === false) {
diff --git a/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc b/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc
new file mode 100644
index 0000000000..d05d27d951
--- /dev/null
+++ b/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc
@@ -0,0 +1,398 @@
+getPos(skip: false),
+ count: count(array_or_countable: $array),
+ value: 50
+);
+
+array_fill(
+ start_index: /* testNestedFunctionCallInner1 */ $obj->getPos(skip: false),
+ count: /* testNestedFunctionCallInner2 */ count(array_or_countable: $array),
+ value: 50
+);
+
+/* testNamespaceOperatorFunction */
+namespace\function_name(label:$string, more: false);
+
+/* testNamespaceRelativeFunction */
+Partially\Qualified\function_name(label:$string, more: false);
+
+/* testNamespacedFQNFunction */
+\Fully\Qualified\function_name(label: $string, more:false);
+
+/* testVariableFunction */
+$fn(label: $string, more:false);
+
+/* testVariableVariableFunction */
+${$fn}(label: $string, more:false);
+
+/* testMethodCall */
+$obj->methodName(label: $foo, more: $bar);
+
+/* testVariableMethodCall */
+$obj->{$var}(label: $foo, more: $bar);
+
+/* testClassInstantiation */
+$obj = new MyClass(label: $string, more:false);
+
+/* testClassInstantiationSelf */
+$obj = new self(label: $string, more:true);
+
+/* testClassInstantiationStatic */
+$obj = new static(label: $string, more:false);
+
+/* testAnonClass */
+$anon = new class(label: $string, more: false) {
+ public function __construct($label, $more) {}
+};
+
+function myfoo( $💩💩💩, $Пасха, $_valid) {}
+/* testNonAsciiNames */
+foo(💩💩💩: [], Пасха: 'text', _valid: 123);
+
+/* testMixedPositionalAndNamedArgsWithTernary */
+foo( $cond ? true : false, name: $value2 );
+
+/* testNamedArgWithTernary */
+foo( label: $cond ? true : false, more: $cond ? CONSTANT_A : CONSTANT_B );
+
+/* testTernaryWithFunctionCallsInThenElse */
+echo $cond ? foo( label: $something ) : foo( more: $something_else );
+
+/* testTernaryWithConstantsInThenElse */
+echo $cond ? CONSTANT_NAME : OTHER_CONSTANT;
+
+switch ($s) {
+ /* testSwitchCaseWithConstant */
+ case MY_CONSTANT:
+ // Do something.
+ break;
+
+ /* testSwitchCaseWithClassProperty */
+ case $obj->property:
+ // Do something.
+ break;
+
+ /* testSwitchDefault */
+ default:
+ // Do something.
+ break;
+}
+
+/* testTernaryWithClosuresAndReturnTypes */
+$closure = $cond ? function() : bool {return true;} : function() : int {return 123;};
+
+/* testTernaryWithArrowFunctionsAndReturnTypes */
+$fn = $cond ? fn() : bool => true : fn() : int => 123;
+
+
+/* testCompileErrorNamedBeforePositional */
+// Not the concern of PHPCS. Should still be handled.
+test(param: $bar, $foo);
+
+/* testDuplicateName1 */
+// Error Exception, but not the concern of PHPCS. Should still be handled.
+test(param: 1, /* testDuplicateName2 */ param: 2);
+
+/* testIncorrectOrderWithVariadic */
+// Error Exception, but not the concern of PHPCS. Should still be handled.
+array_fill(start_index: 0, ...[100, 50]);
+
+/* testCompileErrorIncorrectOrderWithVariadic */
+// Not the concern of PHPCS. Should still be handled.
+test(...$values, param: $value); // Compile-time error
+
+/* testParseErrorNoValue */
+// Not the concern of PHPCS. Should still be handled.
+test(param1:, param2:);
+
+/* testParseErrorDynamicName */
+// Parse error. Ignore.
+function_name($variableStoringParamName: $value);
+
+/* testParseErrorExit */
+// Exit is a language construct, not a function. Named params not supported, handle it anyway.
+exit(status: $value);
+
+/* testParseErrorEmpty */
+// Empty is a language construct, not a function. Named params not supported, handle it anyway.
+empty(variable: $value);
+
+/* testParseErrorEval */
+// Eval is a language construct, not a function. Named params not supported, handle it anyway.
+eval(code: $value);
+
+/* testParseErrorArbitraryParentheses */
+// Parse error. Not named param, handle it anyway.
+$calc = (something: $value / $other);
+
+
+/* testReservedKeywordAbstract1 */
+foobar(abstract: $value, /* testReservedKeywordAbstract2 */ abstract: $value);
+
+/* testReservedKeywordAnd1 */
+foobar(and: $value, /* testReservedKeywordAnd2 */ and: $value);
+
+/* testReservedKeywordArray1 */
+foobar(array: $value, /* testReservedKeywordArray2 */ array: $value);
+
+/* testReservedKeywordAs1 */
+foobar(as: $value, /* testReservedKeywordAs2 */ as: $value);
+
+/* testReservedKeywordBreak1 */
+foobar(break: $value, /* testReservedKeywordBreak2 */ break: $value);
+
+/* testReservedKeywordCallable1 */
+foobar(callable: $value, /* testReservedKeywordCallable2 */ callable: $value);
+
+/* testReservedKeywordCase1 */
+foobar(case: $value, /* testReservedKeywordCase2 */ case: $value);
+
+/* testReservedKeywordCatch1 */
+foobar(catch: $value, /* testReservedKeywordCatch2 */ catch: $value);
+
+/* testReservedKeywordClass1 */
+foobar(class: $value, /* testReservedKeywordClass2 */ class: $value);
+
+/* testReservedKeywordClone1 */
+foobar(clone: $value, /* testReservedKeywordClone2 */ clone: $value);
+
+/* testReservedKeywordConst1 */
+foobar(const: $value, /* testReservedKeywordConst2 */ const: $value);
+
+/* testReservedKeywordContinue1 */
+foobar(continue: $value, /* testReservedKeywordContinue2 */ continue: $value);
+
+/* testReservedKeywordDeclare1 */
+foobar(declare: $value, /* testReservedKeywordDeclare2 */ declare: $value);
+
+/* testReservedKeywordDefault1 */
+foobar(default: $value, /* testReservedKeywordDefault2 */ default: $value);
+
+/* testReservedKeywordDie1 */
+foobar(die: $value, /* testReservedKeywordDie2 */ die: $value);
+
+/* testReservedKeywordDo1 */
+foobar(do: $value, /* testReservedKeywordDo2 */ do: $value);
+
+/* testReservedKeywordEcho1 */
+foobar(echo: $value, /* testReservedKeywordEcho2 */ echo: $value);
+
+/* testReservedKeywordElse1 */
+foobar(else: $value, /* testReservedKeywordElse2 */ else: $value);
+
+/* testReservedKeywordElseif1 */
+foobar(elseif: $value, /* testReservedKeywordElseif2 */ elseif: $value);
+
+/* testReservedKeywordEmpty1 */
+foobar(empty: $value, /* testReservedKeywordEmpty2 */ empty: $value);
+
+/* testReservedKeywordEnddeclare1 */
+foobar(enddeclare: $value, /* testReservedKeywordEnddeclare2 */ enddeclare: $value);
+
+/* testReservedKeywordEndfor1 */
+foobar(endfor: $value, /* testReservedKeywordEndfor2 */ endfor: $value);
+
+/* testReservedKeywordEndforeach1 */
+foobar(endforeach: $value, /* testReservedKeywordEndforeach2 */ endforeach: $value);
+
+/* testReservedKeywordEndif1 */
+foobar(endif: $value, /* testReservedKeywordEndif2 */ endif: $value);
+
+/* testReservedKeywordEndswitch1 */
+foobar(endswitch: $value, /* testReservedKeywordEndswitch2 */ endswitch: $value);
+
+/* testReservedKeywordEndwhile1 */
+foobar(endwhile: $value, /* testReservedKeywordEndwhile2 */ endwhile: $value);
+
+/* testReservedKeywordEval1 */
+foobar(eval: $value, /* testReservedKeywordEval2 */ eval: $value);
+
+/* testReservedKeywordExit1 */
+foobar(exit: $value, /* testReservedKeywordExit2 */ exit: $value);
+
+/* testReservedKeywordExtends1 */
+foobar(extends: $value, /* testReservedKeywordExtends2 */ extends: $value);
+
+/* testReservedKeywordFinal1 */
+foobar(final: $value, /* testReservedKeywordFinal2 */ final: $value);
+
+/* testReservedKeywordFinally1 */
+foobar(finally: $value, /* testReservedKeywordFinally2 */ finally: $value);
+
+/* testReservedKeywordFn1 */
+foobar(fn: $value, /* testReservedKeywordFn2 */ fn: $value);
+
+/* testReservedKeywordFor1 */
+foobar(for: $value, /* testReservedKeywordFor2 */ for: $value);
+
+/* testReservedKeywordForeach1 */
+foobar(foreach: $value, /* testReservedKeywordForeach2 */ foreach: $value);
+
+/* testReservedKeywordFunction1 */
+foobar(function: $value, /* testReservedKeywordFunction2 */ function: $value);
+
+/* testReservedKeywordGlobal1 */
+foobar(global: $value, /* testReservedKeywordGlobal2 */ global: $value);
+
+/* testReservedKeywordGoto1 */
+foobar(goto: $value, /* testReservedKeywordGoto2 */ goto: $value);
+
+/* testReservedKeywordIf1 */
+foobar(if: $value, /* testReservedKeywordIf2 */ if: $value);
+
+/* testReservedKeywordImplements1 */
+foobar(implements: $value, /* testReservedKeywordImplements2 */ implements: $value);
+
+/* testReservedKeywordInclude1 */
+foobar(include: $value, /* testReservedKeywordInclude2 */ include: $value);
+
+/* testReservedKeywordInclude_once1 */
+foobar(include_once: $value, /* testReservedKeywordInclude_once2 */ include_once: $value);
+
+/* testReservedKeywordInstanceof1 */
+foobar(instanceof: $value, /* testReservedKeywordInstanceof2 */ instanceof: $value);
+
+/* testReservedKeywordInsteadof1 */
+foobar(insteadof: $value, /* testReservedKeywordInsteadof2 */ insteadof: $value);
+
+/* testReservedKeywordInterface1 */
+foobar(interface: $value, /* testReservedKeywordInterface2 */ interface: $value);
+
+/* testReservedKeywordIsset1 */
+foobar(isset: $value, /* testReservedKeywordIsset2 */ isset: $value);
+
+/* testReservedKeywordList1 */
+foobar(list: $value, /* testReservedKeywordList2 */ list: $value);
+
+/* testReservedKeywordMatch1 */
+foobar(match: $value, /* testReservedKeywordMatch2 */ match: $value);
+
+/* testReservedKeywordNamespace1 */
+foobar(namespace: $value, /* testReservedKeywordNamespace2 */ namespace: $value);
+
+/* testReservedKeywordNew1 */
+foobar(new: $value, /* testReservedKeywordNew2 */ new: $value);
+
+/* testReservedKeywordOr1 */
+foobar(or: $value, /* testReservedKeywordOr2 */ or: $value);
+
+/* testReservedKeywordPrint1 */
+foobar(print: $value, /* testReservedKeywordPrint2 */ print: $value);
+
+/* testReservedKeywordPrivate1 */
+foobar(private: $value, /* testReservedKeywordPrivate2 */ private: $value);
+
+/* testReservedKeywordProtected1 */
+foobar(protected: $value, /* testReservedKeywordProtected2 */ protected: $value);
+
+/* testReservedKeywordPublic1 */
+foobar(public: $value, /* testReservedKeywordPublic2 */ public: $value);
+
+/* testReservedKeywordRequire1 */
+foobar(require: $value, /* testReservedKeywordRequire2 */ require: $value);
+
+/* testReservedKeywordRequire_once1 */
+foobar(require_once: $value, /* testReservedKeywordRequire_once2 */ require_once: $value);
+
+/* testReservedKeywordReturn1 */
+foobar(return: $value, /* testReservedKeywordReturn2 */ return: $value);
+
+/* testReservedKeywordStatic1 */
+foobar(static: $value, /* testReservedKeywordStatic2 */ static: $value);
+
+/* testReservedKeywordSwitch1 */
+foobar(switch: $value, /* testReservedKeywordSwitch2 */ switch: $value);
+
+/* testReservedKeywordThrow1 */
+foobar(throw: $value, /* testReservedKeywordThrow2 */ throw: $value);
+
+/* testReservedKeywordTrait1 */
+foobar(trait: $value, /* testReservedKeywordTrait2 */ trait: $value);
+
+/* testReservedKeywordTry1 */
+foobar(try: $value, /* testReservedKeywordTry2 */ try: $value);
+
+/* testReservedKeywordUnset1 */
+foobar(unset: $value, /* testReservedKeywordUnset2 */ unset: $value);
+
+/* testReservedKeywordUse1 */
+foobar(use: $value, /* testReservedKeywordUse2 */ use: $value);
+
+/* testReservedKeywordVar1 */
+foobar(var: $value, /* testReservedKeywordVar2 */ var: $value);
+
+/* testReservedKeywordWhile1 */
+foobar(while: $value, /* testReservedKeywordWhile2 */ while: $value);
+
+/* testReservedKeywordXor1 */
+foobar(xor: $value, /* testReservedKeywordXor2 */ xor: $value);
+
+/* testReservedKeywordYield1 */
+foobar(yield: $value, /* testReservedKeywordYield2 */ yield: $value);
+
+/* testReservedKeywordInt1 */
+foobar(int: $value, /* testReservedKeywordInt2 */ int: $value);
+
+/* testReservedKeywordFloat1 */
+foobar(float: $value, /* testReservedKeywordFloat2 */ float: $value);
+
+/* testReservedKeywordBool1 */
+foobar(bool: $value, /* testReservedKeywordBool2 */ bool: $value);
+
+/* testReservedKeywordString1 */
+foobar(string: $value, /* testReservedKeywordString2 */ string: $value);
+
+/* testReservedKeywordTrue1 */
+foobar(true: $value, /* testReservedKeywordTrue2 */ true: $value);
+
+/* testReservedKeywordFalse1 */
+foobar(false: $value, /* testReservedKeywordFalse2 */ false: $value);
+
+/* testReservedKeywordNull1 */
+foobar(null: $value, /* testReservedKeywordNull2 */ null: $value);
+
+/* testReservedKeywordVoid1 */
+foobar(void: $value, /* testReservedKeywordVoid2 */ void: $value);
+
+/* testReservedKeywordIterable1 */
+foobar(iterable: $value, /* testReservedKeywordIterable2 */ iterable: $value);
+
+/* testReservedKeywordObject1 */
+foobar(object: $value, /* testReservedKeywordObject2 */ object: $value);
+
+/* testReservedKeywordResource1 */
+foobar(resource: $value, /* testReservedKeywordResource2 */ resource: $value);
+
+/* testReservedKeywordMixed1 */
+foobar(mixed: $value, /* testReservedKeywordMixed2 */ mixed: $value);
+
+/* testReservedKeywordNumeric1 */
+foobar(numeric: $value, /* testReservedKeywordNumeric2 */ numeric: $value);
+
+/* testReservedKeywordParent1 */
+foobar(parent: $value, /* testReservedKeywordParent2 */ parent: $value);
+
+/* testReservedKeywordSelf1 */
+foobar(self: $value, /* testReservedKeywordSelf2 */ self: $value);
diff --git a/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php b/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php
new file mode 100644
index 0000000000..13be10e5f6
--- /dev/null
+++ b/tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php
@@ -0,0 +1,882 @@
+
+ * @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;
+use PHP_CodeSniffer\Util\Tokens;
+
+class NamedFunctionCallArgumentsTest extends AbstractMethodUnitTest
+{
+
+
+ /**
+ * Verify that parameter labels are tokenized as T_PARAM_NAME and that
+ * the colon after it is tokenized as a T_COLON.
+ *
+ * @param string $testMarker The comment prefacing the target token.
+ * @param array $parameters The token content for each parameter label to look for.
+ *
+ * @dataProvider dataNamedFunctionCallArguments
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testNamedFunctionCallArguments($testMarker, $parameters)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ foreach ($parameters as $content) {
+ $label = $this->getTargetToken($testMarker, [T_STRING, T_PARAM_NAME], $content);
+
+ $this->assertSame(
+ T_PARAM_NAME,
+ $tokens[$label]['code'],
+ 'Token tokenized as '.$tokens[$label]['code'].', not T_PARAM_NAME (code)'
+ );
+ $this->assertSame(
+ 'T_PARAM_NAME',
+ $tokens[$label]['type'],
+ 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (type)'
+ );
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+ }//end foreach
+
+ }//end testNamedFunctionCallArguments()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testNamedFunctionCallArguments()
+ *
+ * @return array
+ */
+ public function dataNamedFunctionCallArguments()
+ {
+ return [
+ [
+ '/* testNamedArgs */',
+ [
+ 'start_index',
+ 'count',
+ 'value',
+ ],
+ ],
+ [
+ '/* testNamedArgsMultiline */',
+ [
+ 'start_index',
+ 'count',
+ 'value',
+ ],
+ ],
+ [
+ '/* testNamedArgsWithWhitespaceAndComments */',
+ [
+ 'start_index',
+ 'count',
+ 'value',
+ ],
+ ],
+ [
+ '/* testMixedPositionalAndNamedArgs */',
+ ['double_encode'],
+ ],
+ [
+ '/* testNestedFunctionCallOuter */',
+ [
+ 'start_index',
+ 'count',
+ 'value',
+ ],
+ ],
+ [
+ '/* testNestedFunctionCallInner1 */',
+ ['skip'],
+ ],
+ [
+ '/* testNestedFunctionCallInner2 */',
+ ['array_or_countable'],
+ ],
+ [
+ '/* testNamespaceOperatorFunction */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testNamespaceRelativeFunction */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testNamespacedFQNFunction */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testVariableFunction */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testVariableVariableFunction */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testMethodCall */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testVariableMethodCall */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testClassInstantiation */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testClassInstantiationSelf */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testClassInstantiationStatic */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testAnonClass */',
+ [
+ 'label',
+ 'more',
+ ],
+ ],
+ [
+ '/* testNonAsciiNames */',
+ [
+ '💩💩💩',
+ 'Пасха',
+ '_valid',
+ ],
+ ],
+
+ // Coding errors which should still be handled.
+ [
+ '/* testCompileErrorNamedBeforePositional */',
+ ['param'],
+ ],
+ [
+ '/* testDuplicateName1 */',
+ ['param'],
+ ],
+ [
+ '/* testDuplicateName2 */',
+ ['param'],
+ ],
+ [
+ '/* testIncorrectOrderWithVariadic */',
+ ['start_index'],
+ ],
+ [
+ '/* testCompileErrorIncorrectOrderWithVariadic */',
+ ['param'],
+ ],
+ [
+ '/* testParseErrorNoValue */',
+ [
+ 'param1',
+ 'param2',
+ ],
+ ],
+ [
+ '/* testParseErrorExit */',
+ ['status'],
+ ],
+ [
+ '/* testParseErrorEmpty */',
+ ['variable'],
+ ],
+ [
+ '/* testParseErrorEval */',
+ ['code'],
+ ],
+ [
+ '/* testParseErrorArbitraryParentheses */',
+ ['something'],
+ ],
+ ];
+
+ }//end dataNamedFunctionCallArguments()
+
+
+ /**
+ * Verify that other T_STRING tokens within a function call are still tokenized as T_STRING.
+ *
+ * @param string $testMarker The comment prefacing the target token.
+ * @param string $content The token content to look for.
+ *
+ * @dataProvider dataOtherTstringInFunctionCall
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testOtherTstringInFunctionCall($testMarker, $content)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $label = $this->getTargetToken($testMarker, [T_STRING, T_PARAM_NAME], $content);
+
+ $this->assertSame(
+ T_STRING,
+ $tokens[$label]['code'],
+ 'Token tokenized as '.$tokens[$label]['code'].', not T_STRING (code)'
+ );
+ $this->assertSame(
+ 'T_STRING',
+ $tokens[$label]['type'],
+ 'Token tokenized as '.$tokens[$label]['type'].', not T_STRING (type)'
+ );
+
+ }//end testOtherTstringInFunctionCall()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testOtherTstringInFunctionCall()
+ *
+ * @return array
+ */
+ public function dataOtherTstringInFunctionCall()
+ {
+ return [
+ [
+ '/* testPositionalArgs */',
+ 'START_INDEX',
+ ],
+ [
+ '/* testPositionalArgs */',
+ 'COUNT',
+ ],
+ [
+ '/* testPositionalArgs */',
+ 'VALUE',
+ ],
+ [
+ '/* testNestedFunctionCallInner2 */',
+ 'count',
+ ],
+ ];
+
+ }//end dataOtherTstringInFunctionCall()
+
+
+ /**
+ * Verify whether the colons are tokenized correctly when a ternary is used in a mixed
+ * positional and named arguments function call.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testMixedPositionalAndNamedArgsWithTernary()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $true = $this->getTargetToken('/* testMixedPositionalAndNamedArgsWithTernary */', T_TRUE);
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true);
+
+ $this->assertSame(
+ T_INLINE_ELSE,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)'
+ );
+ $this->assertSame(
+ 'T_INLINE_ELSE',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)'
+ );
+
+ $label = $this->getTargetToken('/* testMixedPositionalAndNamedArgsWithTernary */', T_PARAM_NAME, 'name');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ }//end testMixedPositionalAndNamedArgsWithTernary()
+
+
+ /**
+ * Verify whether the colons are tokenized correctly when a ternary is used
+ * in a named arguments function call.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testNamedArgWithTernary()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ /*
+ * First argument.
+ */
+
+ $label = $this->getTargetToken('/* testNamedArgWithTernary */', T_PARAM_NAME, 'label');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'First arg: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'First arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'First arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ $true = $this->getTargetToken('/* testNamedArgWithTernary */', T_TRUE);
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true);
+
+ $this->assertSame(
+ T_INLINE_ELSE,
+ $tokens[$colon]['code'],
+ 'First arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)'
+ );
+ $this->assertSame(
+ 'T_INLINE_ELSE',
+ $tokens[$colon]['type'],
+ 'First arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)'
+ );
+
+ /*
+ * Second argument.
+ */
+
+ $label = $this->getTargetToken('/* testNamedArgWithTernary */', T_PARAM_NAME, 'more');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Second arg: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Second arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Second arg: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ $true = $this->getTargetToken('/* testNamedArgWithTernary */', T_STRING, 'CONSTANT_A');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($true + 1), null, true);
+
+ $this->assertSame(
+ T_INLINE_ELSE,
+ $tokens[$colon]['code'],
+ 'Second arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)'
+ );
+ $this->assertSame(
+ 'T_INLINE_ELSE',
+ $tokens[$colon]['type'],
+ 'Second arg ternary: Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)'
+ );
+
+ }//end testNamedArgWithTernary()
+
+
+ /**
+ * Verify whether the colons are tokenized correctly when named arguments
+ * function calls are used in a ternary.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testTernaryWithFunctionCallsInThenElse()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ /*
+ * Then.
+ */
+
+ $label = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_PARAM_NAME, 'label');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Function in then: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Function in then: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Function in then: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ $closeParens = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_CLOSE_PARENTHESIS);
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($closeParens + 1), null, true);
+
+ $this->assertSame(
+ T_INLINE_ELSE,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)'
+ );
+ $this->assertSame(
+ 'T_INLINE_ELSE',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)'
+ );
+
+ /*
+ * Else.
+ */
+
+ $label = $this->getTargetToken('/* testTernaryWithFunctionCallsInThenElse */', T_PARAM_NAME, 'more');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Function in else: Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Function in else: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Function in else: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ }//end testTernaryWithFunctionCallsInThenElse()
+
+
+ /**
+ * Verify whether the colons are tokenized correctly when constants are used in a ternary.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testTernaryWithConstantsInThenElse()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $constant = $this->getTargetToken('/* testTernaryWithConstantsInThenElse */', T_STRING, 'CONSTANT_NAME');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($constant + 1), null, true);
+
+ $this->assertSame(
+ T_INLINE_ELSE,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (code)'
+ );
+ $this->assertSame(
+ 'T_INLINE_ELSE',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_INLINE_ELSE (type)'
+ );
+
+ }//end testTernaryWithConstantsInThenElse()
+
+
+ /**
+ * Verify whether the colons are tokenized correctly in a switch statement.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testSwitchStatement()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $label = $this->getTargetToken('/* testSwitchCaseWithConstant */', T_STRING, 'MY_CONSTANT');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'First case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'First case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ $label = $this->getTargetToken('/* testSwitchCaseWithClassProperty */', T_STRING, 'property');
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Second case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Second case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ $default = $this->getTargetToken('/* testSwitchDefault */', T_DEFAULT);
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($default + 1), null, true);
+
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Default case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Default case: Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ }//end testSwitchStatement()
+
+
+ /**
+ * Verify that a variable parameter label (parse error) is still tokenized as T_VARIABLE.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testParseErrorVariableLabel()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $label = $this->getTargetToken('/* testParseErrorDynamicName */', [T_VARIABLE, T_PARAM_NAME], '$variableStoringParamName');
+
+ $this->assertSame(
+ T_VARIABLE,
+ $tokens[$label]['code'],
+ 'Token tokenized as '.$tokens[$label]['type'].', not T_VARIABLE (code)'
+ );
+ $this->assertSame(
+ 'T_VARIABLE',
+ $tokens[$label]['type'],
+ 'Token tokenized as '.$tokens[$label]['type'].', not T_VARIABLE (type)'
+ );
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ }//end testParseErrorVariableLabel()
+
+
+ /**
+ * Verify that reserved keywords used as a parameter label are tokenized as T_PARAM_NAME
+ * and that the colon after it is tokenized as a T_COLON.
+ *
+ * @param string $testMarker The comment prefacing the target token.
+ * @param array $tokenTypes The token codes to look for.
+ * @param string $tokenContent The token content to look for.
+ *
+ * @dataProvider dataReservedKeywordsAsName
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::tokenize
+ *
+ * @return void
+ */
+ public function testReservedKeywordsAsName($testMarker, $tokenTypes, $tokenContent)
+ {
+ $tokens = self::$phpcsFile->getTokens();
+ $label = $this->getTargetToken($testMarker, $tokenTypes, $tokenContent);
+
+ $this->assertSame(
+ T_PARAM_NAME,
+ $tokens[$label]['code'],
+ 'Token tokenized as '.$tokens[$label]['code'].', not T_PARAM_NAME (code)'
+ );
+ $this->assertSame(
+ 'T_PARAM_NAME',
+ $tokens[$label]['type'],
+ 'Token tokenized as '.$tokens[$label]['type'].', not T_PARAM_NAME (type)'
+ );
+
+ // Get the next non-empty token.
+ $colon = self::$phpcsFile->findNext(Tokens::$emptyTokens, ($label + 1), null, true);
+
+ $this->assertSame(
+ ':',
+ $tokens[$colon]['content'],
+ 'Next token after parameter name is not a colon. Found: '.$tokens[$colon]['content']
+ );
+ $this->assertSame(
+ T_COLON,
+ $tokens[$colon]['code'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (code)'
+ );
+ $this->assertSame(
+ 'T_COLON',
+ $tokens[$colon]['type'],
+ 'Token tokenized as '.$tokens[$colon]['type'].', not T_COLON (type)'
+ );
+
+ }//end testReservedKeywordsAsName()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testReservedKeywordsAsName()
+ *
+ * @return array
+ */
+ public function dataReservedKeywordsAsName()
+ {
+ $reservedKeywords = [
+ // '__halt_compiler', NOT TESTABLE
+ 'abstract',
+ 'and',
+ 'array',
+ 'as',
+ 'break',
+ 'callable',
+ 'case',
+ 'catch',
+ 'class',
+ 'clone',
+ 'const',
+ 'continue',
+ 'declare',
+ 'default',
+ 'die',
+ 'do',
+ 'echo',
+ 'else',
+ 'elseif',
+ 'empty',
+ 'enddeclare',
+ 'endfor',
+ 'endforeach',
+ 'endif',
+ 'endswitch',
+ 'endwhile',
+ 'eval',
+ 'exit',
+ 'extends',
+ 'final',
+ 'finally',
+ 'fn',
+ 'for',
+ 'foreach',
+ 'function',
+ 'global',
+ 'goto',
+ 'if',
+ 'implements',
+ 'include',
+ 'include_once',
+ 'instanceof',
+ 'insteadof',
+ 'interface',
+ 'isset',
+ 'list',
+ 'match',
+ 'namespace',
+ 'new',
+ 'or',
+ 'print',
+ 'private',
+ 'protected',
+ 'public',
+ 'require',
+ 'require_once',
+ 'return',
+ 'static',
+ 'switch',
+ 'throw',
+ 'trait',
+ 'try',
+ 'unset',
+ 'use',
+ 'var',
+ 'while',
+ 'xor',
+ 'yield',
+ 'int',
+ 'float',
+ 'bool',
+ 'string',
+ 'true',
+ 'false',
+ 'null',
+ 'void',
+ 'iterable',
+ 'object',
+ 'resource',
+ 'mixed',
+ 'numeric',
+
+ // Not reserved keyword, but do have their own token in PHPCS.
+ 'parent',
+ 'self',
+ ];
+
+ $data = [];
+
+ foreach ($reservedKeywords as $keyword) {
+ $tokensTypes = [
+ T_PARAM_NAME,
+ T_STRING,
+ T_GOTO_LABEL,
+ ];
+ $tokenName = 'T_'.strtoupper($keyword);
+
+ if ($keyword === 'and') {
+ $tokensTypes[] = T_LOGICAL_AND;
+ } else if ($keyword === 'die') {
+ $tokensTypes[] = T_EXIT;
+ } else if ($keyword === 'or') {
+ $tokensTypes[] = T_LOGICAL_OR;
+ } else if ($keyword === 'xor') {
+ $tokensTypes[] = T_LOGICAL_XOR;
+ } else if ($keyword === '__halt_compiler') {
+ $tokensTypes[] = T_HALT_COMPILER;
+ } else if (defined($tokenName) === true) {
+ $tokensTypes[] = constant($tokenName);
+ }
+
+ $data[$keyword.'FirstParam'] = [
+ '/* testReservedKeyword'.ucfirst($keyword).'1 */',
+ $tokensTypes,
+ $keyword,
+ ];
+
+ $data[$keyword.'SecondParam'] = [
+ '/* testReservedKeyword'.ucfirst($keyword).'2 */',
+ $tokensTypes,
+ $keyword,
+ ];
+ }//end foreach
+
+ return $data;
+
+ }//end dataReservedKeywordsAsName()
+
+
+}//end class