Skip to content

Commit df8bfe9

Browse files
committed
Tokenizer/PHP: bug fix - "yield" vs function return by ref (PHP 5.4)
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.
1 parent 7507442 commit df8bfe9

File tree

3 files changed

+25
-20
lines changed

3 files changed

+25
-20
lines changed

src/Tokenizers/PHP.php

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,27 @@ protected function tokenize($string)
605605
echo PHP_EOL;
606606
}
607607

608+
/*
609+
Before PHP 5.5, the yield keyword was tokenized as
610+
T_STRING. So look for and change this token in
611+
earlier versions.
612+
*/
613+
614+
if (PHP_VERSION_ID < 50500
615+
&& $tokenIsArray === true
616+
&& $token[0] === T_STRING
617+
&& strtolower($token[1]) === 'yield'
618+
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
619+
) {
620+
// Could still be a context sensitive keyword or "yield from" and potentially multi-line,
621+
// so adjust the token stack in place.
622+
$token[0] = T_YIELD;
623+
624+
if (PHP_CODESNIFFER_VERBOSITY > 1) {
625+
echo "\t\t* token $stackPtr changed from T_STRING to T_YIELD".PHP_EOL;
626+
}
627+
}
628+
608629
/*
609630
Tokenize context sensitive keyword as string when it should be string.
610631
*/
@@ -1492,26 +1513,6 @@ protected function tokenize($string)
14921513
continue;
14931514
}//end if
14941515

1495-
/*
1496-
Before PHP 5.5, the yield keyword was tokenized as
1497-
T_STRING. So look for and change this token in
1498-
earlier versions.
1499-
*/
1500-
1501-
if (PHP_VERSION_ID < 50500
1502-
&& $tokenIsArray === true
1503-
&& $token[0] === T_STRING
1504-
&& strtolower($token[1]) === 'yield'
1505-
&& isset($this->tstringContexts[$finalTokens[$lastNotEmptyToken]['code']]) === false
1506-
) {
1507-
// Could still be "yield from" and potentially multi-line, so adjust the token stack.
1508-
$token[0] = T_YIELD;
1509-
1510-
if (PHP_CODESNIFFER_VERBOSITY > 1) {
1511-
echo "\t\t* token $stackPtr changed from T_STRING to T_YIELD".PHP_EOL;
1512-
}
1513-
}
1514-
15151516
/*
15161517
Before PHP 7.0, the "yield from" was tokenized as
15171518
T_YIELD, T_WHITESPACE and T_STRING. So look for

tests/Core/Tokenizers/PHP/YieldTest.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ class Yield {
4242
/* testFromUsedForClassConstantAccess1 */
4343
echo MyClass::FROM;
4444
}
45+
46+
/* testYieldUsedAsMethodNameReturnByRef */
47+
public function &yield() {}
4548
}
4649

4750
function myGen() {

tests/Core/Tokenizers/PHP/YieldTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ public static function dataYieldNonKeyword()
232232
'yield used as property access 2' => ['/* testYieldUsedAsPropertyName2 */'],
233233
'yield used as class constant access' => ['/* testYieldUsedForClassConstantAccess1 */'],
234234
'from used as class constant access' => ['/* testFromUsedForClassConstantAccess1 */'],
235+
'yield used as method name with ref' => ['/* testYieldUsedAsMethodNameReturnByRef */'],
235236
];
236237

237238
}//end dataYieldNonKeyword()

0 commit comments

Comments
 (0)