Commit cad6ed2a388 for php.net
commit cad6ed2a388d0d85b913d1b44e298b536b61e1c3
Author: Tim Düsterhus <tim@bastelstu.be>
Date: Sun Jun 21 21:50:52 2026 +0200
zend_ast: Surround function by parens when exporting calls to function stored in property (#22376)
* zend_ast: Surround function by parens when exporting calls to function stored in property
The extra parentheses are needed to disambiguate method calls from calls to a
function stored in a property.
Fixes php/php-src#22373.
* zend_ast: Avoid needless indirection through `zend_ast_export_ns_name()`
diff --git a/NEWS b/NEWS
index c7645ca27b8..858ca396189 100644
--- a/NEWS
+++ b/NEWS
@@ -28,6 +28,8 @@ PHP NEWS
invalid variable names). (timwolla)
. Fixed bug GH-22291 (AST pretty printing does not correctly handle braces
in string interpolation). (timwolla)
+ . Fixed bug GH-22373 (AST pretty-printing drops meaningful parentheses
+ surrounding property access). (timwolla)
- BCMath:
. Added NUL-byte validation to BCMath functions. (jorgsowa)
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index f495c4c8e3b..57faedc06f9 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -2535,12 +2535,18 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
break;
case ZEND_AST_CALL: {
zend_ast *left = ast->child[0];
- if (left->kind == ZEND_AST_ARROW_FUNC || left->kind == ZEND_AST_CLOSURE) {
- smart_str_appendc(str, '(');
- zend_ast_export_ns_name(str, left, 0, indent);
- smart_str_appendc(str, ')');
- } else {
- zend_ast_export_ns_name(str, left, 0, indent);
+ switch (left->kind) {
+ /* ZEND_AST_ZVAL is a regular function call. */
+ case ZEND_AST_ZVAL:
+ /* ZEND_AST_VAR ($foo()) is unambiguous without parens. */
+ case ZEND_AST_VAR:
+ zend_ast_export_ns_name(str, left, 0, indent);
+ break;
+ default:
+ smart_str_appendc(str, '(');
+ zend_ast_export_ex(str, left, 0, indent);
+ smart_str_appendc(str, ')');
+ break;
}
smart_str_appendc(str, '(');
zend_ast_export_ex(str, ast->child[1], 0, indent);
diff --git a/ext/standard/tests/assert/gh22373.phpt b/ext/standard/tests/assert/gh22373.phpt
new file mode 100644
index 00000000000..8c26f77f490
--- /dev/null
+++ b/ext/standard/tests/assert/gh22373.phpt
@@ -0,0 +1,36 @@
+--TEST--
+GH-22373: AST pretty-printing drops meaningful parentheses surrounding property access
+--FILE--
+<?php
+
+class Foo {
+ public static Closure $sf = strrev(...);
+
+ public function __construct(
+ public Closure $f = strrev(...),
+ ) {
+ try {
+ assert(($this->f)('abc') !== 'cba');
+ } catch (Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+ }
+ try {
+ assert(($this?->f)('abc') !== 'cba');
+ } catch (Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+ }
+ try {
+ assert((self::$sf)('abc') !== 'cba');
+ } catch (Error $e) {
+ echo $e->getMessage(), PHP_EOL;
+ }
+ }
+}
+
+new Foo();
+
+?>
+--EXPECT--
+assert(($this->f)('abc') !== 'cba')
+assert(($this?->f)('abc') !== 'cba')
+assert((self::$sf)('abc') !== 'cba')