Skip to content

Commit 11d11dc

Browse files
committed
Address a few review comments
1 parent f37e9d7 commit 11d11dc

File tree

4 files changed

+90
-9
lines changed

4 files changed

+90
-9
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
--TEST--
2+
Test that __clone() can write to readonly properties
3+
--FILE--
4+
<?php
5+
6+
class Counter
7+
{
8+
private static int $counter = 0;
9+
10+
public readonly int $count;
11+
private readonly int $foo;
12+
13+
public function __construct()
14+
{
15+
$this->count = ++self::$counter;
16+
$this->foo = 0;
17+
}
18+
19+
public function count(?int $count = null): static
20+
{
21+
$new = clone $this;
22+
$new->count = $count ?? ++self::$counter;
23+
24+
return $new;
25+
}
26+
27+
public function __clone()
28+
{
29+
if (is_a(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2)[1]['class'] ?? '', self::class, true)) {
30+
unset($this->count);
31+
} else {
32+
$this->count = ++self::$counter;
33+
}
34+
$this->foo = 1;
35+
}
36+
}
37+
38+
$a = new Counter();
39+
var_dump($a);
40+
41+
var_dump(clone $a);
42+
43+
$b = $a->count();
44+
var_dump($b);
45+
46+
$c = $a->count(123);
47+
var_dump($c);
48+
49+
?>
50+
--EXPECTF--
51+
object(Counter)#%d (2) {
52+
["count"]=>
53+
int(1)
54+
["foo":"Counter":private]=>
55+
int(0)
56+
}
57+
object(Counter)#%d (2) {
58+
["count"]=>
59+
int(2)
60+
["foo":"Counter":private]=>
61+
int(1)
62+
}
63+
object(Counter)#%d (2) {
64+
["count"]=>
65+
int(3)
66+
["foo":"Counter":private]=>
67+
int(1)
68+
}
69+
object(Counter)#%d (2) {
70+
["count"]=>
71+
int(123)
72+
["foo":"Counter":private]=>
73+
int(1)
74+
}

Zend/zend_API.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1703,7 +1703,6 @@ static zend_always_inline zend_result _object_and_properties_init(zval *arg, zen
17031703
} else {
17041704
ZVAL_OBJ(arg, class_type->create_object(class_type));
17051705
}
1706-
17071706
return SUCCESS;
17081707
}
17091708
/* }}} */

Zend/zend_execute.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,8 +983,12 @@ static zend_never_inline zval* zend_assign_to_typed_prop(zend_property_info *inf
983983
zval tmp;
984984

985985
if (UNEXPECTED(info->flags & ZEND_ACC_READONLY)) {
986-
zend_readonly_property_modification_error(info);
987-
return &EG(uninitialized_zval);
986+
if (Z_PROP_FLAG_P(property_val) & IS_PROP_REINITABLE) {
987+
Z_PROP_FLAG_P(property_val) &= ~IS_PROP_REINITABLE;
988+
} else {
989+
zend_readonly_property_modification_error(info);
990+
return &EG(uninitialized_zval);
991+
}
988992
}
989993

990994
ZVAL_DEREF(value);
@@ -3116,6 +3120,9 @@ static zend_always_inline void zend_fetch_property_address(zval *result, zval *c
31163120
ZEND_ASSERT(type == BP_VAR_W || type == BP_VAR_RW || type == BP_VAR_UNSET);
31173121
if (Z_TYPE_P(ptr) == IS_OBJECT) {
31183122
ZVAL_COPY(result, ptr);
3123+
} else if (Z_PROP_FLAG_P(ptr) & IS_PROP_REINITABLE) {
3124+
Z_PROP_FLAG_P(ptr) &= ~IS_PROP_REINITABLE;
3125+
ZVAL_COPY(result, ptr);
31193126
} else {
31203127
zend_readonly_property_modification_error(prop_info);
31213128
ZVAL_ERROR(result);

Zend/zend_objects.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -263,15 +263,16 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object,
263263
}
264264

265265
if (has_clone_method) {
266-
zval *prop;
267-
268266
GC_ADDREF(new_object);
269267
zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL);
270268

271-
rebuild_object_properties(new_object);
272-
ZEND_HASH_FOREACH_PTR(new_object->properties, prop) {
273-
Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE;
274-
} ZEND_HASH_FOREACH_END();
269+
for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) {
270+
zend_property_info *prop_info = new_object->ce->properties_info_table[i];
271+
if (prop_info && (prop_info->flags & (~ZEND_ACC_STATIC|ZEND_ACC_READONLY)) && ZEND_TYPE_IS_SET(prop_info->type)) {
272+
zval *prop = OBJ_PROP_NUM(new_object, i);
273+
Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE;
274+
}
275+
}
275276

276277
OBJ_RELEASE(new_object);
277278
}

0 commit comments

Comments
 (0)