From b4ba8f6ee788ffc2151d043381dcce200e2b30c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Sat, 13 Jun 2026 14:55:58 +0200 Subject: [PATCH 1/2] zend_ast: Always print encaps list variables with braces when exporting AST The `"{$foo}"` variant of interpolating variables into a string is the only one that reliably works all the time. Always use it for simplicity. Fixes php/php-src#22291. --- Zend/tests/assert/expect_015.phpt | 8 ++++---- Zend/tests/assert/expect_020.phpt | 2 +- Zend/zend_ast.c | 26 -------------------------- ext/standard/tests/assert/gh22291.phpt | 19 +++++++++++++++++++ 4 files changed, 24 insertions(+), 31 deletions(-) create mode 100644 ext/standard/tests/assert/gh22291.phpt diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt index 695c4c166a83..b4716d8b4b31 100644 --- a/Zend/tests/assert/expect_015.phpt +++ b/Zend/tests/assert/expect_015.phpt @@ -201,7 +201,7 @@ assert(0 && ($a = function &(array &$a, ?X $b = null) use($c, &$d): ?X { $s[$i] = $a[$j]; } foreach ($a as $key => &$val) { - print "$key => $val\n"; + print "{$key} => {$val}\n"; } while ($s[$i]) { $i++; @@ -299,12 +299,12 @@ assert(0 && ($a = function (): ?static { echo 1; } $x = '\'"`$a'; - $x = "'\"`$a"; - $x = `'"\`$a`; + $x = "'\"`{$a}"; + $x = `'"\`{$a}`; $x = "{$a}b"; $x = "{$a}b"; $x = " {$foo->bar} {${$foo->bar}} "; - $x = " ${---} "; + $x = " {${---}} "; foo(); \foo(); namespace\foo(); diff --git a/Zend/tests/assert/expect_020.phpt b/Zend/tests/assert/expect_020.phpt index d305667a28d7..2146abc96045 100644 --- a/Zend/tests/assert/expect_020.phpt +++ b/Zend/tests/assert/expect_020.phpt @@ -16,5 +16,5 @@ assert(0 && ($a = function () { --EXPECT-- assert(): assert(0 && ($a = function () { $var = 'test'; - $str = "$var, {$var[1]}, {$var}[], {$var[1]}[], {$var}[], {$var[1]}[]"; + $str = "{$var}, {$var[1]}, {$var}[], {$var[1]}[], {$var}[], {$var[1]}[]"; })) failed diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 9df2320d5666..2afbabd6dc84 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1367,19 +1367,6 @@ static ZEND_COLD void zend_ast_export_ns_name(smart_str *str, zend_ast *ast, int zend_ast_export_ex(str, ast, priority, indent); } -static ZEND_COLD bool zend_ast_valid_var_char(char ch) -{ - unsigned char c = (unsigned char)ch; - - if (c != '_' && c < 127 && - (c < '0' || c > '9') && - (c < 'A' || c > 'Z') && - (c < 'a' || c > 'z')) { - return 0; - } - return 1; -} - static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len) { unsigned char c; @@ -1406,11 +1393,6 @@ static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len) return 1; } -static ZEND_COLD bool zend_ast_var_needs_braces(char ch) -{ - return ch == '[' || zend_ast_valid_var_char(ch); -} - static ZEND_COLD void zend_ast_export_var(smart_str *str, zend_ast *ast, int priority, int indent) { if (ast->kind == ZEND_AST_ZVAL) { @@ -1454,14 +1436,6 @@ static ZEND_COLD void zend_ast_export_encaps_list(smart_str *str, char quote, ze ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); zend_ast_export_qstr(str, quote, Z_STR_P(zv)); - } else if (ast->kind == ZEND_AST_VAR && - ast->child[0]->kind == ZEND_AST_ZVAL && - (i + 1 == list->children || - list->child[i + 1]->kind != ZEND_AST_ZVAL || - !zend_ast_var_needs_braces( - *Z_STRVAL_P( - zend_ast_get_zval(list->child[i + 1]))))) { - zend_ast_export_ex(str, ast, 0, indent); } else { smart_str_appendc(str, '{'); zend_ast_export_ex(str, ast, 0, indent); diff --git a/ext/standard/tests/assert/gh22291.phpt b/ext/standard/tests/assert/gh22291.phpt new file mode 100644 index 000000000000..a50bd5861f05 --- /dev/null +++ b/ext/standard/tests/assert/gh22291.phpt @@ -0,0 +1,19 @@ +--TEST-- +GH-22291: AST pretty printing does not correctly handle braces in string interpolation +--FILE-- +getMessage(), PHP_EOL; +} + +?> +--EXPECT-- +string(5) "{abc}" +string(3) "abc" +assert(!"{{$foo}}") From bbf3e5272c718010304bbb77afbf59a19257d14d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 15 Jun 2026 22:47:41 +0200 Subject: [PATCH 2/2] NEWS --- NEWS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS b/NEWS index efc85058719e..e947b61b9d4c 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,8 @@ PHP NEWS (kocsismate) . Fixed bug GH-22292 (AST pretty printing does not correctly handle invalid variable names). (timwolla) + . Fixed bug GH-22291 (AST pretty printing does not correctly handle braces + in string interpolation). (timwolla) - BCMath: . Added NUL-byte validation to BCMath functions. (jorgsowa)