From 73d05f83968a51c87eec08e6b48810cc625173d9 Mon Sep 17 00:00:00 2001 From: Jorg Sowa Date: Fri, 30 Oct 2026 20:47:46 +0100 Subject: [PATCH] Fix AST export dropping parentheses in static property, method and constant fetches --- NEWS | 3 ++ .../assert/ast_export_static_parens.phpt | 35 +++++++++++++++++++ Zend/zend_ast.c | 8 ++--- 3 files changed, 42 insertions(+), 4 deletions(-) create mode 100644 Zend/tests/assert/ast_export_static_parens.phpt diff --git a/NEWS b/NEWS index 798f849854c1..af3a52755b67 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,9 @@ PHP NEWS (Marti2203). . Fixed bug GH-24989 (Use-after-free in GC when an exception is thrown). (David Carlier) + . Fixed AST export dropping parentheses around the class operand of static + property fetches, static method calls and class constant fetches. + (Jorg Sowa) - CLI: . Fixed bug GH-24697 (PHP_HTTP_PARSER_STRICT build option doesn't work). diff --git a/Zend/tests/assert/ast_export_static_parens.phpt b/Zend/tests/assert/ast_export_static_parens.phpt new file mode 100644 index 000000000000..788bbd71c03b --- /dev/null +++ b/Zend/tests/assert/ast_export_static_parens.phpt @@ -0,0 +1,35 @@ +--TEST-- +assert() message keeps parentheses around the class operand of :: +--INI-- +zend.assertions=1 +--FILE-- + assert(($a ?? $c)::$x), + fn() => assert(($a ?? $c)::m()), + fn() => assert(($a ?? $c)::X), + fn() => assert(($a ?? $c)::class === 'D'), +]; + +foreach ($tests as $test) { + try { + $test(); + } catch (AssertionError $e) { + echo $e::class, ': ', $e->getMessage(), "\n"; + } +} +?> +--EXPECT-- +AssertionError: assert(($a ?? $c)::$x) +AssertionError: assert(($a ?? $c)::m()) +AssertionError: assert(($a ?? $c)::X) +AssertionError: assert(($a ?? $c)::class === 'D') diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 4e640a321d1f..fc50a21c3eb1 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -2205,7 +2205,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_var(str, ast->child[1], 0, indent); break; case ZEND_AST_STATIC_PROP: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name(str, ast->child[0], 260, indent); smart_str_appends(str, "::$"); zend_ast_export_var(str, ast->child[1], 0, indent); break; @@ -2225,7 +2225,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "..."); break; case ZEND_AST_CLASS_CONST: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name(str, ast->child[0], 260, indent); smart_str_appends(str, "::"); zend_ast_export_name(str, ast->child[1], 0, indent); break; @@ -2242,7 +2242,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio EMPTY_SWITCH_DEFAULT_CASE() } } else { - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name(str, ast->child[0], 260, indent); } smart_str_appends(str, "::class"); break; @@ -2547,7 +2547,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appendc(str, ')'); break; case ZEND_AST_STATIC_CALL: - zend_ast_export_ns_name(str, ast->child[0], 0, indent); + zend_ast_export_ns_name(str, ast->child[0], 260, indent); smart_str_appends(str, "::"); zend_ast_export_var(str, ast->child[1], 0, indent); smart_str_appendc(str, '(');