@@ -73,6 +73,13 @@ typedef struct _spl_array_object {
73
73
zend_object std ;
74
74
} spl_array_object ;
75
75
76
+ typedef struct _spl_array_iterator {
77
+ zend_object_iterator it ;
78
+ zend_class_entry * ce ;
79
+ zval value ;
80
+ bool by_ref ;
81
+ } spl_array_iterator ;
82
+
76
83
static inline spl_array_object * spl_array_from_obj (zend_object * obj ) /* {{{ */ {
77
84
return (spl_array_object * )((char * )(obj ) - XtOffsetOf (spl_array_object , std ));
78
85
}
@@ -1007,18 +1014,42 @@ static int spl_array_it_valid(zend_object_iterator *iter) /* {{{ */
1007
1014
1008
1015
static zval * spl_array_it_get_current_data (zend_object_iterator * iter ) /* {{{ */
1009
1016
{
1017
+ spl_array_iterator * array_iter = (spl_array_iterator * )iter ;
1010
1018
spl_array_object * object = Z_SPLARRAY_P (& iter -> data );
1011
1019
HashTable * aht = spl_array_get_hash_table (object );
1012
1020
1021
+ zval * data ;
1013
1022
if (object -> ar_flags & SPL_ARRAY_OVERLOADED_CURRENT ) {
1014
- return zend_user_it_get_current_data (iter );
1023
+ data = zend_user_it_get_current_data (iter );
1015
1024
} else {
1016
- zval * data = zend_hash_get_current_data_ex (aht , spl_array_get_pos_ptr (aht , object ));
1025
+ data = zend_hash_get_current_data_ex (aht , spl_array_get_pos_ptr (aht , object ));
1017
1026
if (data && Z_TYPE_P (data ) == IS_INDIRECT ) {
1018
1027
data = Z_INDIRECT_P (data );
1019
1028
}
1020
- return data ;
1021
1029
}
1030
+ // ZEND_FE_FETCH_RW converts the value to a reference but doesn't know the source is a property.
1031
+ // Typed properties must add a type source to the reference, and readonly properties must fail.
1032
+ if (array_iter -> by_ref
1033
+ && Z_TYPE_P (data ) != IS_REFERENCE
1034
+ && !(object -> ar_flags & SPL_ARRAY_IS_SELF )
1035
+ && !(object -> ar_flags & SPL_ARRAY_USE_OTHER )
1036
+ && Z_TYPE (object -> array ) == IS_OBJECT ) {
1037
+ zend_string * key ;
1038
+ zend_hash_get_current_key_ex (aht , & key , NULL , spl_array_get_pos_ptr (aht , object ));
1039
+ zend_class_entry * ce = Z_OBJCE (object -> array );
1040
+ zend_property_info * prop_info = zend_get_property_info (ce , key , true);
1041
+ if (ZEND_TYPE_IS_SET (prop_info -> type )) {
1042
+ if (prop_info -> flags & ZEND_ACC_READONLY ) {
1043
+ zend_throw_error (NULL ,
1044
+ "Cannot acquire reference to readonly property %s::$%s" ,
1045
+ ZSTR_VAL (prop_info -> ce -> name ), ZSTR_VAL (key ));
1046
+ return NULL ;
1047
+ }
1048
+ ZVAL_NEW_REF (data , data );
1049
+ ZEND_REF_ADD_TYPE_SOURCE (Z_REF_P (data ), prop_info );
1050
+ }
1051
+ }
1052
+ return data ;
1022
1053
}
1023
1054
/* }}} */
1024
1055
@@ -1156,22 +1187,23 @@ static const zend_object_iterator_funcs spl_array_it_funcs = {
1156
1187
1157
1188
zend_object_iterator * spl_array_get_iterator (zend_class_entry * ce , zval * object , int by_ref ) /* {{{ */
1158
1189
{
1159
- zend_user_iterator * iterator ;
1190
+ spl_array_iterator * iterator ;
1160
1191
spl_array_object * array_object = Z_SPLARRAY_P (object );
1161
1192
1162
1193
if (by_ref && (array_object -> ar_flags & SPL_ARRAY_OVERLOADED_CURRENT )) {
1163
1194
zend_throw_error (NULL , "An iterator cannot be used with foreach by reference" );
1164
1195
return NULL ;
1165
1196
}
1166
1197
1167
- iterator = emalloc (sizeof (zend_user_iterator ));
1198
+ iterator = emalloc (sizeof (spl_array_iterator ));
1168
1199
1169
1200
zend_iterator_init (& iterator -> it );
1170
1201
1171
1202
ZVAL_OBJ_COPY (& iterator -> it .data , Z_OBJ_P (object ));
1172
1203
iterator -> it .funcs = & spl_array_it_funcs ;
1173
1204
iterator -> ce = ce ;
1174
1205
ZVAL_UNDEF (& iterator -> value );
1206
+ iterator -> by_ref = by_ref ;
1175
1207
1176
1208
return & iterator -> it ;
1177
1209
}
0 commit comments