885 lines
26 KiB
PHP
885 lines
26 KiB
PHP
<?php
|
|
/**
|
|
* Tests the backfilling of the T_FN token to PHP < 7.4.
|
|
*
|
|
* @author Juliette Reinders Folmer <phpcs_nospam@adviesenzo.nl>
|
|
* @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]['type'].', 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]['type'].', 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]['type'].', 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',
|
|
'readonly',
|
|
'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',
|
|
'never',
|
|
|
|
// 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
|