@@ -73,6 +73,13 @@ typedef struct _spl_array_object {
7373 zend_object std ;
7474} spl_array_object ;
7575
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+
7683static inline spl_array_object * spl_array_from_obj (zend_object * obj ) /* {{{ */ {
7784 return (spl_array_object * )((char * )(obj ) - XtOffsetOf (spl_array_object , std ));
7885}
@@ -1007,18 +1014,41 @@ static int spl_array_it_valid(zend_object_iterator *iter) /* {{{ */
10071014
10081015static zval * spl_array_it_get_current_data (zend_object_iterator * iter ) /* {{{ */
10091016{
1017+ spl_array_iterator * array_iter = (spl_array_iterator * )iter ;
10101018 spl_array_object * object = Z_SPLARRAY_P (& iter -> data );
10111019 HashTable * aht = spl_array_get_hash_table (object );
10121020
1021+ zval * data ;
10131022 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 );
10151024 } 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 ));
10171026 if (data && Z_TYPE_P (data ) == IS_INDIRECT ) {
10181027 data = Z_INDIRECT_P (data );
10191028 }
1020- return data ;
10211029 }
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 ;
10221052}
10231053/* }}} */
10241054
@@ -1156,22 +1186,23 @@ static const zend_object_iterator_funcs spl_array_it_funcs = {
11561186
11571187zend_object_iterator * spl_array_get_iterator (zend_class_entry * ce , zval * object , int by_ref ) /* {{{ */
11581188{
1159- zend_user_iterator * iterator ;
1189+ spl_array_iterator * iterator ;
11601190 spl_array_object * array_object = Z_SPLARRAY_P (object );
11611191
11621192 if (by_ref && (array_object -> ar_flags & SPL_ARRAY_OVERLOADED_CURRENT )) {
11631193 zend_throw_error (NULL , "An iterator cannot be used with foreach by reference" );
11641194 return NULL ;
11651195 }
11661196
1167- iterator = emalloc (sizeof (zend_user_iterator ));
1197+ iterator = emalloc (sizeof (* iterator ));
11681198
11691199 zend_iterator_init (& iterator -> it );
11701200
11711201 ZVAL_OBJ_COPY (& iterator -> it .data , Z_OBJ_P (object ));
11721202 iterator -> it .funcs = & spl_array_it_funcs ;
11731203 iterator -> ce = ce ;
11741204 ZVAL_UNDEF (& iterator -> value );
1205+ iterator -> by_ref = by_ref ;
11751206
11761207 return & iterator -> it ;
11771208}
0 commit comments