Skip to content

Commit cb764c7

Browse files
committed
Tokenizer/PHP: more context sensitive keyword fixes
PHPCS re-tokenizes the `self`, `parent`, `true`, `false` and `null` keywords to a PHPCS native token. This re-tokenization did not take the following situations into account: * Those keywords being used as function names when the function is declared to return by reference. * Those keywords being used as a function call. Additionally, the PHP native `T_STATIC` token would not be (re-)tokenized to `T_STRING` when used as a function call, though it was tokenized correctly when used as a method call.. While using the `static` keyword for a global function declaration is illegal in PHP, the tokenization in PHPCS should still be consistent. This commit fixes those issues. Includes additional unit tests.
1 parent ed8e00d commit cb764c7

File tree

3 files changed

+419
-252
lines changed

3 files changed

+419
-252
lines changed

src/Tokenizers/PHP.php

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,36 @@ protected function tokenize($string)
681681
}
682682
}//end if
683683

684+
/*
685+
Special case for `static` used as a function name, i.e. `static()`.
686+
*/
687+
688+
if ($tokenIsArray === true
689+
&& $token[0] === T_STATIC
690+
&& $finalTokens[$lastNotEmptyToken]['code'] !== T_NEW
691+
) {
692+
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
693+
if (is_array($tokens[$i]) === true
694+
&& isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === true
695+
) {
696+
continue;
697+
}
698+
699+
if ($tokens[$i][0] === '(') {
700+
$finalTokens[$newStackPtr] = [
701+
'code' => T_STRING,
702+
'type' => 'T_STRING',
703+
'content' => $token[1],
704+
];
705+
706+
$newStackPtr++;
707+
continue 2;
708+
}
709+
710+
break;
711+
}
712+
}//end if
713+
684714
/*
685715
Parse doc blocks into something that can be easily iterated over.
686716
*/
@@ -2103,38 +2133,60 @@ function return types. We want to keep the parenthesis map clean,
21032133
}
21042134
} else {
21052135
// Some T_STRING tokens should remain that way due to their context.
2106-
if ($tokenIsArray === true
2107-
&& $token[0] === T_STRING
2108-
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === true
2109-
) {
2110-
// Special case for syntax like: return new self/new parent
2111-
// where self/parent should not be a string.
2112-
$tokenContentLower = strtolower($token[1]);
2113-
if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
2114-
&& ($tokenContentLower === 'self' || $tokenContentLower === 'parent')
2115-
) {
2116-
$finalTokens[$newStackPtr] = [
2117-
'content' => $token[1],
2118-
];
2119-
if ($tokenContentLower === 'self') {
2120-
$finalTokens[$newStackPtr]['code'] = T_SELF;
2121-
$finalTokens[$newStackPtr]['type'] = 'T_SELF';
2136+
if ($tokenIsArray === true && $token[0] === T_STRING) {
2137+
$preserveTstring = false;
2138+
2139+
if (isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === true) {
2140+
$preserveTstring = true;
2141+
2142+
// Special case for syntax like: return new self/new parent
2143+
// where self/parent should not be a string.
2144+
$tokenContentLower = strtolower($token[1]);
2145+
if ($finalTokens[$lastNotEmptyToken]['code'] === T_NEW
2146+
&& ($tokenContentLower === 'self' || $tokenContentLower === 'parent')
2147+
) {
2148+
$preserveTstring = false;
21222149
}
2150+
} else if ($finalTokens[$lastNotEmptyToken]['content'] === '&') {
2151+
// Function names for functions declared to return by reference.
2152+
for ($i = ($lastNotEmptyToken - 1); $i >= 0; $i--) {
2153+
if (isset(Util\Tokens::$emptyTokens[$finalTokens[$i]['code']]) === true) {
2154+
continue;
2155+
}
2156+
2157+
if ($finalTokens[$i]['code'] === T_FUNCTION) {
2158+
$preserveTstring = true;
2159+
}
21232160

2124-
if ($tokenContentLower === 'parent') {
2125-
$finalTokens[$newStackPtr]['code'] = T_PARENT;
2126-
$finalTokens[$newStackPtr]['type'] = 'T_PARENT';
2161+
break;
21272162
}
21282163
} else {
2164+
// Keywords with special PHPCS token when used as a function call.
2165+
for ($i = ($stackPtr + 1); $i < $numTokens; $i++) {
2166+
if (is_array($tokens[$i]) === true
2167+
&& isset(Util\Tokens::$emptyTokens[$tokens[$i][0]]) === true
2168+
) {
2169+
continue;
2170+
}
2171+
2172+
if ($tokens[$i][0] === '(') {
2173+
$preserveTstring = true;
2174+
}
2175+
2176+
break;
2177+
}
2178+
}//end if
2179+
2180+
if ($preserveTstring === true) {
21292181
$finalTokens[$newStackPtr] = [
2130-
'content' => $token[1],
21312182
'code' => T_STRING,
21322183
'type' => 'T_STRING',
2184+
'content' => $token[1],
21332185
];
2134-
}
21352186

2136-
$newStackPtr++;
2137-
continue;
2187+
$newStackPtr++;
2188+
continue;
2189+
}
21382190
}//end if
21392191

21402192
$newToken = null;

0 commit comments

Comments
 (0)