Skip to content

Commit 0859719

Browse files
committed
Merge branch 'php-8/backfill-named-function-call-parameters-tokenization' of https://github.com/jrfnl/PHP_CodeSniffer
2 parents 7f5df08 + 845335a commit 0859719

File tree

5 files changed

+1410
-45
lines changed

5 files changed

+1410
-45
lines changed

package.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
172172
<file baseinstalldir="" name="BitwiseOrTest.php" role="test" />
173173
<file baseinstalldir="" name="GotoLabelTest.inc" role="test" />
174174
<file baseinstalldir="" name="GotoLabelTest.php" role="test" />
175+
<file baseinstalldir="" name="NamedFunctionCallArgumentsTest.inc" role="test" />
176+
<file baseinstalldir="" name="NamedFunctionCallArgumentsTest.php" role="test" />
175177
<file baseinstalldir="" name="NullsafeObjectOperatorTest.inc" role="test" />
176178
<file baseinstalldir="" name="NullsafeObjectOperatorTest.php" role="test" />
177179
<file baseinstalldir="" name="ScopeSettingWithNamespaceOperatorTest.inc" role="test" />
@@ -2074,6 +2076,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
20742076
<install as="CodeSniffer/Core/Tokenizer/BitwiseOrTest.inc" name="tests/Core/Tokenizer/BitwiseOrTest.inc" />
20752077
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />
20762078
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.inc" name="tests/Core/Tokenizer/GotoLabelTest.inc" />
2079+
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" />
2080+
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc" />
20772081
<install as="CodeSniffer/Core/Tokenizer/NullsafeObjectOperatorTest.php" name="tests/Core/Tokenizer/NullsafeObjectOperatorTest.php" />
20782082
<install as="CodeSniffer/Core/Tokenizer/NullsafeObjectOperatorTest.inc" name="tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc" />
20792083
<install as="CodeSniffer/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php" name="tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php" />
@@ -2148,6 +2152,8 @@ http://pear.php.net/dtd/package-2.0.xsd">
21482152
<install as="CodeSniffer/Core/Tokenizer/BitwiseOrTest.inc" name="tests/Core/Tokenizer/BitwiseOrTest.inc" />
21492153
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.php" name="tests/Core/Tokenizer/GotoLabelTest.php" />
21502154
<install as="CodeSniffer/Core/Tokenizer/GotoLabelTest.inc" name="tests/Core/Tokenizer/GotoLabelTest.inc" />
2155+
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.php" />
2156+
<install as="CodeSniffer/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc" name="tests/Core/Tokenizer/NamedFunctionCallArgumentsTest.inc" />
21512157
<install as="CodeSniffer/Core/Tokenizer/NullsafeObjectOperatorTest.php" name="tests/Core/Tokenizer/NullsafeObjectOperatorTest.php" />
21522158
<install as="CodeSniffer/Core/Tokenizer/NullsafeObjectOperatorTest.inc" name="tests/Core/Tokenizer/NullsafeObjectOperatorTest.inc" />
21532159
<install as="CodeSniffer/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php" name="tests/Core/Tokenizer/ScopeSettingWithNamespaceOperatorTest.php" />

src/Tokenizers/PHP.php

Lines changed: 123 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,62 @@ protected function tokenize($string)
893893
continue;
894894
}//end if
895895

896+
/*
897+
Tokenize the parameter labels for PHP 8.0 named parameters as a special T_PARAM_NAME
898+
token and ensure that the colon after it is always T_COLON.
899+
*/
900+
901+
if ($tokenIsArray === true
902+
&& preg_match('`^[a-zA-Z_\x80-\xff]`', $token[1]) === 1
903+
) {
904+
// Get the next non-empty token.
905+
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
906+
if (is_array($tokens[$i]) === false
907+
|| isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === false
908+
) {
909+
break;
910+
}
911+
}
912+
913+
if (isset($tokens[$i]) === true
914+
&& is_array($tokens[$i]) === false
915+
&& $tokens[$i] === ':'
916+
) {
917+
// Get the previous non-empty token.
918+
for ($j = ($stackPtr - 1); $j > 0; $j--) {
919+
if (is_array($tokens[$j]) === false
920+
|| isset(Util\Tokens::$emptyTokens[$tokens[$j][0]]) === false
921+
) {
922+
break;
923+
}
924+
}
925+
926+
if (is_array($tokens[$j]) === false
927+
&& ($tokens[$j] === '('
928+
|| $tokens[$j] === ',')
929+
) {
930+
$newToken = [];
931+
$newToken['code'] = T_PARAM_NAME;
932+
$newToken['type'] = 'T_PARAM_NAME';
933+
$newToken['content'] = $token[1];
934+
$finalTokens[$newStackPtr] = $newToken;
935+
936+
$newStackPtr++;
937+
938+
// Modify the original token stack so that future checks, like
939+
// determining T_COLON vs T_INLINE_ELSE can handle this correctly.
940+
$tokens[$stackPtr][0] = T_PARAM_NAME;
941+
942+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
943+
$type = Util\Tokens::tokenName($token[0]);
944+
echo "\t\t* token $stackPtr changed from $type to T_PARAM_NAME".PHP_EOL;
945+
}
946+
947+
continue;
948+
}
949+
}//end if
950+
}//end if
951+
896952
/*
897953
Before PHP 7.0, the "yield from" was tokenized as
898954
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,
17011757
// Convert colons that are actually the ELSE component of an
17021758
// inline IF statement.
17031759
if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) {
1704-
// Make sure this isn't a return type separator.
17051760
$isInlineIf = true;
1761+
1762+
// Make sure this isn't a named parameter label.
1763+
// Get the previous non-empty token.
17061764
for ($i = ($stackPtr - 1); $i > 0; $i--) {
17071765
if (is_array($tokens[$i]) === false
1708-
|| ($tokens[$i][0] !== T_DOC_COMMENT
1709-
&& $tokens[$i][0] !== T_COMMENT
1710-
&& $tokens[$i][0] !== T_WHITESPACE)
1766+
|| isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === false
17111767
) {
17121768
break;
17131769
}
17141770
}
17151771

1716-
if ($tokens[$i] === ')') {
1717-
$parenCount = 1;
1718-
for ($i--; $i > 0; $i--) {
1719-
if ($tokens[$i] === '(') {
1720-
$parenCount--;
1721-
if ($parenCount === 0) {
1722-
break;
1723-
}
1724-
} else if ($tokens[$i] === ')') {
1725-
$parenCount++;
1726-
}
1772+
if ($tokens[$i][0] === T_PARAM_NAME) {
1773+
$isInlineIf = false;
1774+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1775+
echo "\t\t* token is parameter label, not T_INLINE_ELSE".PHP_EOL;
17271776
}
1777+
}
17281778

1729-
// We've found the open parenthesis, so if the previous
1730-
// non-empty token is FUNCTION or USE, this is a return type.
1731-
// Note that we need to skip T_STRING tokens here as these
1732-
// can be function names.
1733-
for ($i--; $i > 0; $i--) {
1779+
if ($isInlineIf === true) {
1780+
// Make sure this isn't a return type separator.
1781+
for ($i = ($stackPtr - 1); $i > 0; $i--) {
17341782
if (is_array($tokens[$i]) === false
17351783
|| ($tokens[$i][0] !== T_DOC_COMMENT
17361784
&& $tokens[$i][0] !== T_COMMENT
1737-
&& $tokens[$i][0] !== T_WHITESPACE
1738-
&& $tokens[$i][0] !== T_STRING)
1785+
&& $tokens[$i][0] !== T_WHITESPACE)
17391786
) {
17401787
break;
17411788
}
17421789
}
17431790

1744-
if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_FN || $tokens[$i][0] === T_USE) {
1745-
$isInlineIf = false;
1746-
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1747-
echo "\t\t* token is return type, not T_INLINE_ELSE".PHP_EOL;
1791+
if ($tokens[$i] === ')') {
1792+
$parenCount = 1;
1793+
for ($i--; $i > 0; $i--) {
1794+
if ($tokens[$i] === '(') {
1795+
$parenCount--;
1796+
if ($parenCount === 0) {
1797+
break;
1798+
}
1799+
} else if ($tokens[$i] === ')') {
1800+
$parenCount++;
1801+
}
17481802
}
1749-
}
1803+
1804+
// We've found the open parenthesis, so if the previous
1805+
// non-empty token is FUNCTION or USE, this is a return type.
1806+
// Note that we need to skip T_STRING tokens here as these
1807+
// can be function names.
1808+
for ($i--; $i > 0; $i--) {
1809+
if (is_array($tokens[$i]) === false
1810+
|| ($tokens[$i][0] !== T_DOC_COMMENT
1811+
&& $tokens[$i][0] !== T_COMMENT
1812+
&& $tokens[$i][0] !== T_WHITESPACE
1813+
&& $tokens[$i][0] !== T_STRING)
1814+
) {
1815+
break;
1816+
}
1817+
}
1818+
1819+
if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_FN || $tokens[$i][0] === T_USE) {
1820+
$isInlineIf = false;
1821+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1822+
echo "\t\t* token is return type, not T_INLINE_ELSE".PHP_EOL;
1823+
}
1824+
}
1825+
}//end if
17501826
}//end if
17511827

17521828
// Check to see if this is a CASE or DEFAULT opener.
1753-
$inlineIfToken = $insideInlineIf[(count($insideInlineIf) - 1)];
1754-
for ($i = $stackPtr; $i > $inlineIfToken; $i--) {
1755-
if (is_array($tokens[$i]) === true
1756-
&& ($tokens[$i][0] === T_CASE
1757-
|| $tokens[$i][0] === T_DEFAULT)
1758-
) {
1759-
$isInlineIf = false;
1760-
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1761-
echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE".PHP_EOL;
1762-
}
1829+
if ($isInlineIf === true) {
1830+
$inlineIfToken = $insideInlineIf[(count($insideInlineIf) - 1)];
1831+
for ($i = $stackPtr; $i > $inlineIfToken; $i--) {
1832+
if (is_array($tokens[$i]) === true
1833+
&& ($tokens[$i][0] === T_CASE
1834+
|| $tokens[$i][0] === T_DEFAULT)
1835+
) {
1836+
$isInlineIf = false;
1837+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1838+
echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE".PHP_EOL;
1839+
}
17631840

1764-
break;
1765-
}
1841+
break;
1842+
}
17661843

1767-
if (is_array($tokens[$i]) === false
1768-
&& ($tokens[$i] === ';'
1769-
|| $tokens[$i] === '{')
1770-
) {
1771-
break;
1844+
if (is_array($tokens[$i]) === false
1845+
&& ($tokens[$i] === ';'
1846+
|| $tokens[$i] === '{')
1847+
) {
1848+
break;
1849+
}
17721850
}
1773-
}
1851+
}//end if
17741852

17751853
if ($isInlineIf === true) {
17761854
array_pop($insideInlineIf);

src/Util/Tokens.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
define('T_ZSR_EQUAL', 'PHPCS_T_ZSR_EQUAL');
7777
define('T_FN_ARROW', 'T_FN_ARROW');
7878
define('T_TYPE_UNION', 'T_TYPE_UNION');
79+
define('T_PARAM_NAME', 'T_PARAM_NAME');
7980

8081
// Some PHP 5.5 tokens, replicated for lower versions.
8182
if (defined('T_FINALLY') === false) {

0 commit comments

Comments
 (0)