Commit e2c5b3c684f for php.net

commit e2c5b3c684fddb7c22a47f6e518435176ec715fb
Author: NickSdot <32384907+NickSdot@users.noreply.github.com>
Date:   Mon Jul 13 13:52:27 2026 +0700

    zend_objects: Readonly properties must be re-locked after clone-with (#22654)

diff --git a/NEWS b/NEWS
index 46f23c3aa6d..ed10d3bf259 100644
--- a/NEWS
+++ b/NEWS
@@ -7,6 +7,8 @@ PHP                                                                        NEWS
     containing NUL). (iliaal)
   . Fixed bug GH-22206 (missing return in global register detection).
     (P3p111n0)
+  . Lock unmodified readonly properties for modification after clone-with.
+    (NickSdot)

 - Calendar:
   . Fixed bug GH-22602 (gregoriantojd() and juliantojd() integer overflow with
diff --git a/Zend/tests/clone/clone_with_014.phpt b/Zend/tests/clone/clone_with_014.phpt
new file mode 100644
index 00000000000..bfbee40e163
--- /dev/null
+++ b/Zend/tests/clone/clone_with_014.phpt
@@ -0,0 +1,41 @@
+--TEST--
+Properties are still readonly after clone-with
+--FILE--
+<?php
+
+readonly class Test {
+    public public(set) int $a;
+    public public(set) int $b;
+
+    public function __construct() {
+        $this->a = 1;
+        $this->b = 2;
+    }
+}
+
+$test = clone(new Test(), ['a' => 3]);
+var_dump($test);
+
+try {
+    $test->b = 4;
+} catch (Error $e) {
+    echo $e::class, ": ", $e->getMessage(), PHP_EOL;
+}
+
+var_dump($test);
+
+?>
+--EXPECT--
+object(Test)#2 (2) {
+  ["a"]=>
+  int(3)
+  ["b"]=>
+  int(2)
+}
+Error: Cannot modify readonly property Test::$b
+object(Test)#2 (2) {
+  ["a"]=>
+  int(3)
+  ["b"]=>
+  int(2)
+}
diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c
index 6f6a8263894..d92d54d3f4a 100644
--- a/Zend/zend_objects.c
+++ b/Zend/zend_objects.c
@@ -322,6 +322,14 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const
 		} ZEND_HASH_FOREACH_END();

 		EG(fake_scope) = old_scope;
+
+		/* Lock readonly properties once more. */
+		if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) {
+			for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
+				zval* prop = OBJ_PROP_NUM(new_object, i);
+				Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE;
+			}
+		}
 	}

 	return new_object;