Summary
In v0.12.22, Psy\CodeAnalysis\BufferAnalyzer::hasBalancedBrackets() returns false for any buffer containing "…{$var}…" style string interpolation, because the analyzer treats T_CURLY_OPEN (the {$ opener) as an array-form token and skips it, but the closing } arrives as a single-char token and falls through into the bracket counter — so each interpolation registers as one extra unmatched }.
Because StatementCompletenessPolicy::isCompleteStatement() consults hasBalancedBrackets() whenever there's no parse error, any complete buffer containing interpolation is classified as incomplete. With the new readline (legacy or experimental), Enter then keeps inserting line breaks instead of submitting, and the prompt is stuck on . indefinitely.
Minimal repro
$a = new \Psy\CodeAnalysis\BufferAnalyzer();
var_dump($a->analyze('echo "hi {$x}";')->hasBalancedBrackets());
// bool(false) — expected bool(true)
It also surfaces interactively as soon as you paste any multi-line block containing "…{$var}…". e.g.
foreach ($items as $r) {
echo "rehashed {$r->id}\n";
}
…enters the buffer fine, but Enter never submits. Adding "" at the end happens to flip the policy through a different branch (canBeFixedWithSemicolon) and submits the whole thing — accidental escape hatch, not a fix.
Environment
- PsySH v0.12.22
- PHP 8.5.5 (also reproducible on 8.3 / 8.4 since the issue is in static token analysis)
- Linux, kitty terminal — but the bug is terminal-agnostic; the
BufferAnalyzer::analyze(…) call alone reproduces it
Suspected fix
In BufferAnalyzer::hasBalancedBrackets(), the bracket walker needs to skip closing } tokens that match an earlier T_CURLY_OPEN/T_DOLLAR_OPEN_CURLY_BRACES opener (which are array-form tokens that the current implementation skips). Easiest is to track string-interpolation depth alongside the structural-bracket stack.
Summary
In v0.12.22,
Psy\CodeAnalysis\BufferAnalyzer::hasBalancedBrackets()returnsfalsefor any buffer containing"…{$var}…"style string interpolation, because the analyzer treatsT_CURLY_OPEN(the{$opener) as an array-form token and skips it, but the closing}arrives as a single-char token and falls through into the bracket counter — so each interpolation registers as one extra unmatched}.Because
StatementCompletenessPolicy::isCompleteStatement()consultshasBalancedBrackets()whenever there's no parse error, any complete buffer containing interpolation is classified as incomplete. With the new readline (legacy or experimental), Enter then keeps inserting line breaks instead of submitting, and the prompt is stuck on.indefinitely.Minimal repro
It also surfaces interactively as soon as you paste any multi-line block containing
"…{$var}…". e.g.…enters the buffer fine, but Enter never submits. Adding
""at the end happens to flip the policy through a different branch (canBeFixedWithSemicolon) and submits the whole thing — accidental escape hatch, not a fix.Environment
BufferAnalyzer::analyze(…)call alone reproduces itSuspected fix
In
BufferAnalyzer::hasBalancedBrackets(), the bracket walker needs to skip closing}tokens that match an earlierT_CURLY_OPEN/T_DOLLAR_OPEN_CURLY_BRACESopener (which are array-form tokens that the current implementation skips). Easiest is to track string-interpolation depth alongside the structural-bracket stack.