Commit dee1317c333 for php.net
commit dee1317c3334ca624bab6b03b3e4e0c64e90c0a5
Author: Tim Düsterhus <tim@bastelstu.be>
Date: Tue Jun 16 00:14:37 2026 +0200
zend_ast: Always print encaps list variables with braces when exporting AST (#22293)
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.
diff --git a/NEWS b/NEWS
index efc85058719..e947b61b9d4 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)
diff --git a/Zend/tests/assert/expect_015.phpt b/Zend/tests/assert/expect_015.phpt
index 79ea3703ead..3bb08504f58 100644
--- a/Zend/tests/assert/expect_015.phpt
+++ b/Zend/tests/assert/expect_015.phpt
@@ -201,7 +201,7 @@ private static function f1() {
$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 @@ class A {
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 d305667a28d..2146abc9604 100644
--- a/Zend/tests/assert/expect_020.phpt
+++ b/Zend/tests/assert/expect_020.phpt
@@ -16,5 +16,5 @@
--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 983299c0a9d..c908f6b18c7 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -1675,19 +1675,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 false;
- }
- return true;
-}
-
static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len)
{
unsigned char c;
@@ -1714,11 +1701,6 @@ static ZEND_COLD bool zend_ast_valid_var_name(const char *s, size_t len)
return true;
}
-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 indent)
{
if (ast->kind == ZEND_AST_ZVAL) {
@@ -1775,14 +1757,6 @@ static ZEND_COLD void zend_ast_export_encaps_list(smart_str *str, char quote, co
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 00000000000..a50bd5861f0
--- /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--
+<?php
+
+try {
+ $foo = 'abc';
+ var_dump("{{$foo}}");
+ var_dump("{$foo}");
+ assert(!"{{$foo}}");
+} catch (Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+}
+
+?>
+--EXPECT--
+string(5) "{abc}"
+string(3) "abc"
+assert(!"{{$foo}}")