@@ -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,41 @@ 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
+ && Z_TYPE (object -> array ) == IS_OBJECT
1035
+ && !(object -> ar_flags & (SPL_ARRAY_IS_SELF |SPL_ARRAY_USE_OTHER ))) {
1036
+ zend_string * key ;
1037
+ zend_hash_get_current_key_ex (aht , & key , NULL , spl_array_get_pos_ptr (aht , object ));
1038
+ zend_class_entry * ce = Z_OBJCE (object -> array );
1039
+ zend_property_info * prop_info = zend_get_property_info (ce , key , true);
1040
+ if (ZEND_TYPE_IS_SET (prop_info -> type )) {
1041
+ if (prop_info -> flags & ZEND_ACC_READONLY ) {
1042
+ zend_throw_error (NULL ,
1043
+ "Cannot acquire reference to readonly property %s::$%s" ,
1044
+ ZSTR_VAL (prop_info -> ce -> name ), ZSTR_VAL (key ));
1045
+ return NULL ;
1046
+ }
1047
+ ZVAL_NEW_REF (data , data );
1048
+ ZEND_REF_ADD_TYPE_SOURCE (Z_REF_P (data ), prop_info );
1049
+ }
1050
+ }
1051
+ return data ;
1022
1052
}
1023
1053
/* }}} */
1024
1054
@@ -1156,22 +1186,23 @@ static const zend_object_iterator_funcs spl_array_it_funcs = {
1156
1186
1157
1187
zend_object_iterator * spl_array_get_iterator (zend_class_entry * ce , zval * object , int by_ref ) /* {{{ */
1158
1188
{
1159
- zend_user_iterator * iterator ;
1189
+ spl_array_iterator * iterator ;
1160
1190
spl_array_object * array_object = Z_SPLARRAY_P (object );
1161
1191
1162
1192
if (by_ref && (array_object -> ar_flags & SPL_ARRAY_OVERLOADED_CURRENT )) {
1163
1193
zend_throw_error (NULL , "An iterator cannot be used with foreach by reference" );
1164
1194
return NULL ;
1165
1195
}
1166
1196
1167
- iterator = emalloc (sizeof (zend_user_iterator ));
1197
+ iterator = emalloc (sizeof (* iterator ));
1168
1198
1169
1199
zend_iterator_init (& iterator -> it );
1170
1200
1171
1201
ZVAL_OBJ_COPY (& iterator -> it .data , Z_OBJ_P (object ));
1172
1202
iterator -> it .funcs = & spl_array_it_funcs ;
1173
1203
iterator -> ce = ce ;
1174
1204
ZVAL_UNDEF (& iterator -> value );
1205
+ iterator -> by_ref = by_ref ;
1175
1206
1176
1207
return & iterator -> it ;
1177
1208
}
0 commit comments