Commit 8a8f4752a3b for php

commit 8a8f4752a3b877265f8788bc121e20f9c1a2c7b8
Author: Jorg Sowa <jorg.sowa@gmail.com>
Date:   Wed Nov 11 10:30:50 2026 +0100

    Fix AST printing dropping readonly on promoted parameters (GH-25207)

    Closes GH-25207

diff --git a/NEWS b/NEWS
index 2986eaec7c3..9397cd352ab 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,8 @@ PHP                                                                        NEWS
   . Fixed use-after-free when the error handler releases a string while an
     "Illegal string offset" warning is raised for its offset in a read, ?? or
     nested isset()/empty(). (Jorg Sowa)
+  . Fixed AST printing dropping readonly on promoted constructor parameters.
+    (Jorg Sowa)

 - Curl:
   . Fixed curl_reset() retaining CURLOPT_PRIVATE after reset. (lacatoire)
diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c
index 7d6038a194d..0f20becf045 100644
--- a/Zend/zend_ast.c
+++ b/Zend/zend_ast.c
@@ -2627,6 +2627,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
 				zend_ast_export_attributes(str, ast->child[3], indent, 0);
 			}
 			zend_ast_export_visibility(str, ast->attr, ZEND_MODIFIER_TARGET_CPP);
+			if (ast->attr & ZEND_ACC_READONLY) {
+				smart_str_appends(str, "readonly ");
+			}
 			if (ast->child[0]) {
 				zend_ast_export_type(str, ast->child[0], indent);
 				smart_str_appendc(str, ' ');
diff --git a/ext/zend_test/tests/compile_to_ast/readonly_promoted.phpt b/ext/zend_test/tests/compile_to_ast/readonly_promoted.phpt
new file mode 100644
index 00000000000..c038ce47d21
--- /dev/null
+++ b/ext/zend_test/tests/compile_to_ast/readonly_promoted.phpt
@@ -0,0 +1,28 @@
+--TEST--
+AST can be recreated (readonly promoted constructor parameters)
+--EXTENSIONS--
+zend_test
+--FILE--
+<?php
+
+class Foo {
+    public function __construct(
+        public readonly int $publicReadonly,
+        readonly int $implicitlyPublicReadonly,
+        protected private(set) readonly array $asymmetricReadonly = [],
+        #[Attr] private readonly ?string $attributedReadonly = null,
+        public int $notReadonly = 1,
+    ) {}
+}
+
+echo zend_test_compile_to_ast( file_get_contents( __FILE__ ) );
+
+?>
+--EXPECT--
+class Foo {
+    public function __construct(public readonly int $publicReadonly, readonly int $implicitlyPublicReadonly, protected private(set) readonly array $asymmetricReadonly = [], #[Attr] private readonly ?string $attributedReadonly = null, public int $notReadonly = 1) {
+    }
+
+}
+
+echo zend_test_compile_to_ast(file_get_contents(__FILE__));