Skip to content

Commit 8505acd

Browse files
committed
Correctly handle ?-> in encapsed strings
Followup upstream change.
1 parent c3e20d9 commit 8505acd

File tree

4 files changed

+51
-13
lines changed

4 files changed

+51
-13
lines changed

lib/PhpParser/Lexer/TokenEmulator/NullsafeTokenEmulator.php

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,35 @@ public function emulate(string $code, array $tokens): array
2222
// the tokens array on the way
2323
$line = 1;
2424
for ($i = 0, $c = count($tokens); $i < $c; ++$i) {
25-
if (isset($tokens[$i + 1])) {
26-
if ($tokens[$i] === '?' && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
27-
array_splice($tokens, $i, 2, [
28-
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
29-
]);
30-
$c--;
31-
continue;
25+
if ($tokens[$i] === '?' && isset($tokens[$i + 1]) && $tokens[$i + 1][0] === \T_OBJECT_OPERATOR) {
26+
array_splice($tokens, $i, 2, [
27+
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line]
28+
]);
29+
$c--;
30+
continue;
31+
}
32+
33+
// Handle ?-> inside encapsed string.
34+
if ($tokens[$i][0] === \T_ENCAPSED_AND_WHITESPACE && isset($tokens[$i - 1])
35+
&& $tokens[$i - 1][0] === \T_VARIABLE
36+
&& preg_match('/^\?->([a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*)/', $tokens[$i][1], $matches)
37+
) {
38+
$replacement = [
39+
[\T_NULLSAFE_OBJECT_OPERATOR, '?->', $line],
40+
[\T_STRING, $matches[1], $line],
41+
];
42+
if (\strlen($matches[0]) !== \strlen($tokens[$i][1])) {
43+
$replacement[] = [
44+
\T_ENCAPSED_AND_WHITESPACE,
45+
\substr($tokens[$i][1], \strlen($matches[0])),
46+
$line
47+
];
3248
}
49+
array_splice($tokens, $i, 1, $replacement);
50+
$c += \count($replacement) - 1;
51+
continue;
3352
}
53+
3454
if (\is_array($tokens[$i])) {
3555
$line += substr_count($tokens[$i][1], "\n");
3656
}

test/PhpParser/Lexer/EmulativeTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,22 @@ public function provideTestTargetVersion() {
320320
['7.4', 'match', [[Tokens::T_STRING, 'match']]],
321321
['7.4', 'fn', [[Tokens::T_FN, 'fn']]],
322322
['7.3', 'fn', [[Tokens::T_STRING, 'fn']]],
323+
// Tested here to skip testLeaveStuffAloneInStrings.
324+
['8.0', '"$foo?->bar"', [
325+
[ord('"'), '"'],
326+
[Tokens::T_VARIABLE, '$foo'],
327+
[Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'],
328+
[Tokens::T_STRING, 'bar'],
329+
[ord('"'), '"'],
330+
]],
331+
['8.0', '"$foo?->bar baz"', [
332+
[ord('"'), '"'],
333+
[Tokens::T_VARIABLE, '$foo'],
334+
[Tokens::T_NULLSAFE_OBJECT_OPERATOR, '?->'],
335+
[Tokens::T_STRING, 'bar'],
336+
[Tokens::T_ENCAPSED_AND_WHITESPACE, ' baz'],
337+
[ord('"'), '"'],
338+
]],
323339
];
324340
}
325341
}

test/code/parser/expr/nullsafe.test

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,13 @@ array(
7171
4: Stmt_Expression(
7272
expr: Scalar_Encapsed(
7373
parts: array(
74-
0: Expr_Variable(
75-
name: a
76-
)
77-
1: Scalar_EncapsedStringPart(
78-
value: ?->b
74+
0: Expr_NullsafePropertyFetch(
75+
var: Expr_Variable(
76+
name: a
77+
)
78+
name: Identifier(
79+
name: b
80+
)
7981
)
8082
)
8183
)

test/code/prettyPrinter/expr/nullsafe.test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ $a?->b($c)?->d;
1919
$a?->b($c)();
2020
new $a?->b();
2121
"{$a?->b}";
22-
"{$a}?->b";
22+
"{$a?->b}";

0 commit comments

Comments
 (0)