Skip to content

Commit 93371d3

Browse files
committed
Dirty solution for ref mod to typed and readonly props through ArrayIterator
Fixes GH-10844
1 parent 7d987eb commit 93371d3

File tree

3 files changed

+87
-3
lines changed

3 files changed

+87
-3
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Test typed property modification by-ref through ArrayIterator
3+
--FILE--
4+
<?php
5+
class A implements IteratorAggregate {
6+
function __construct(
7+
public string $foo = 'bar'
8+
) {}
9+
10+
function getIterator(): Traversable {
11+
return new ArrayIterator($this);
12+
}
13+
}
14+
15+
$obj = new A;
16+
foreach ($obj as $k => &$v) {
17+
$v = 42;
18+
}
19+
20+
var_dump($obj);
21+
?>
22+
--EXPECT--
23+
object(A)#1 (1) {
24+
["foo"]=>
25+
&string(2) "42"
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
--TEST--
2+
Test readonly property modification by-ref through ArrayIterator
3+
--FILE--
4+
<?php
5+
class A implements IteratorAggregate {
6+
function __construct(
7+
public readonly string $foo = 'bar'
8+
) {}
9+
10+
function getIterator(): Traversable {
11+
return new ArrayIterator($this);
12+
}
13+
}
14+
15+
$obj = new A;
16+
foreach ($obj as $k => &$v) {
17+
$v = 'baz';
18+
}
19+
20+
var_dump($obj);
21+
?>
22+
--EXPECTF--
23+
Fatal error: Uncaught Error: Cannot acquire reference to readonly property A::$foo in %s:%d
24+
Stack trace:
25+
#0 {main}
26+
thrown in %s on line %d

ext/spl/spl_array.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,15 +1010,47 @@ static zval *spl_array_it_get_current_data(zend_object_iterator *iter) /* {{{ */
10101010
spl_array_object *object = Z_SPLARRAY_P(&iter->data);
10111011
HashTable *aht = spl_array_get_hash_table(object);
10121012

1013+
zval *data;
10131014
if (object->ar_flags & SPL_ARRAY_OVERLOADED_CURRENT) {
1014-
return zend_user_it_get_current_data(iter);
1015+
data = zend_user_it_get_current_data(iter);
10151016
} else {
1016-
zval *data = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, object));
1017+
data = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, object));
10171018
if (data && Z_TYPE_P(data) == IS_INDIRECT) {
10181019
data = Z_INDIRECT_P(data);
10191020
}
1020-
return data;
10211021
}
1022+
// ZEND_FE_FETCH_RW converts the value to a reference but doesn't know the source is a property.
1023+
// Typed properties must add a type source to the reference, and readonly properties must fail.
1024+
zend_execute_data *execute_data = EG(current_execute_data);
1025+
if (data
1026+
&& Z_TYPE_P(data) != IS_REFERENCE
1027+
&& execute_data
1028+
&& ZEND_USER_CODE(execute_data->func->type)
1029+
&& execute_data->opline
1030+
&& execute_data->opline->opcode == ZEND_FE_FETCH_RW
1031+
&& !(object->ar_flags & SPL_ARRAY_IS_SELF)
1032+
&& !(object->ar_flags & SPL_ARRAY_USE_OTHER)
1033+
&& Z_TYPE(object->array) == IS_OBJECT) {
1034+
zend_string *key;
1035+
zend_hash_get_current_key_ex(aht, &key, NULL, spl_array_get_pos_ptr(aht, object));
1036+
zend_class_entry *ce = Z_OBJCE(object->array);
1037+
zend_property_info *prop_info = zend_get_property_info(ce, key, true);
1038+
if (ZEND_TYPE_IS_SET(prop_info->type)) {
1039+
if (prop_info->flags & ZEND_ACC_READONLY) {
1040+
zend_throw_error(NULL,
1041+
"Cannot acquire reference to readonly property %s::$%s",
1042+
ZSTR_VAL(prop_info->ce->name), ZSTR_VAL(key));
1043+
return NULL;
1044+
}
1045+
zend_refcounted *gc = Z_COUNTED_P(data);
1046+
zval *ref;
1047+
ZVAL_NEW_EMPTY_REF(data);
1048+
ref = Z_REFVAL_P(data);
1049+
ZVAL_COPY_VALUE_EX(ref, data, gc, Z_TYPE_P(data));
1050+
ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), prop_info);
1051+
}
1052+
}
1053+
return data;
10221054
}
10231055
/* }}} */
10241056

0 commit comments

Comments
 (0)