Skip to content

Commit

Permalink
Tokenizer/PHP: bug fix - "yield" vs function return by ref (PHP 5.4)
Browse files Browse the repository at this point in the history
On PHP 5.4, where PHP doesn't natively have the `T_YIELD` token yet, a `yield` function name for a function declared to return by reference would be tokenized as `T_YIELD`, instead of `T_STRING` due to the `T_YIELD` polyfill happening _after_ the context sensitive keyword check has already run.

By moving the check for a `T_STRING` "yield" keyword up to above the check for context sensitive keywords, this bug is fixed.

Includes additional test.
  • Loading branch information
jrfnl committed Oct 25, 2024
1 parent 7507442 commit df8bfe9
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 20 deletions.
41 changes: 21 additions & 20 deletions src/Tokenizers/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,27 @@ protected function tokenize($string)
echo PHP_EOL;
}

/*
Before PHP 5.5, the yield keyword was tokenized as
T_STRING. So look for and change this token in
earlier versions.
*/

if (PHP_VERSION_ID < 50500
&& $tokenIsArray === true
&& $token[0] === T_STRING
&& strtolower($token[1]) === 'yield'
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
) {
// Could still be a context sensitive keyword or "yield from" and potentially multi-line,
// so adjust the token stack in place.
$token[0] = T_YIELD;

if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t\t* token $stackPtr changed from T_STRING to T_YIELD".PHP_EOL;
}
}

/*
Tokenize context sensitive keyword as string when it should be string.
*/
Expand Down Expand Up @@ -1492,26 +1513,6 @@ protected function tokenize($string)
continue;
}//end if

/*
Before PHP 5.5, the yield keyword was tokenized as
T_STRING. So look for and change this token in
earlier versions.
*/

if (PHP_VERSION_ID < 50500
&& $tokenIsArray === true
&& $token[0] === T_STRING
&& strtolower($token[1]) === 'yield'
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
) {
// Could still be "yield from" and potentially multi-line, so adjust the token stack.
$token[0] = T_YIELD;

if (PHP_CODESNIFFER_VERBOSITY > 1) {
echo "\t\t* token $stackPtr changed from T_STRING to T_YIELD".PHP_EOL;
}
}

/*
Before PHP 7.0, the "yield from" was tokenized as
T_YIELD, T_WHITESPACE and T_STRING. So look for
Expand Down
3 changes: 3 additions & 0 deletions tests/Core/Tokenizers/PHP/YieldTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ class Yield {
/* testFromUsedForClassConstantAccess1 */
echo MyClass::FROM;
}

/* testYieldUsedAsMethodNameReturnByRef */
public function &yield() {}
}

function myGen() {
Expand Down
1 change: 1 addition & 0 deletions tests/Core/Tokenizers/PHP/YieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ public static function dataYieldNonKeyword()
'yield used as property access 2' => ['/* testYieldUsedAsPropertyName2 */'],
'yield used as class constant access' => ['/* testYieldUsedForClassConstantAccess1 */'],
'from used as class constant access' => ['/* testFromUsedForClassConstantAccess1 */'],
'yield used as method name with ref' => ['/* testYieldUsedAsMethodNameReturnByRef */'],
];

}//end dataYieldNonKeyword()
Expand Down

0 comments on commit df8bfe9

Please sign in to comment.