diff --git a/Zend/tests/type_declarations/typed_properties_113.phpt b/Zend/tests/type_declarations/typed_properties_113.phpt new file mode 100644 index 00000000000..cb5c0f92764 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_113.phpt @@ -0,0 +1,26 @@ +--TEST-- +Typed property type coercion through ArrayIterator +--FILE-- + &$v) { + $v = 42; +} + +var_dump($obj); +?> +--EXPECT-- +object(A)#1 (1) { + ["foo"]=> + &string(2) "42" +} diff --git a/Zend/tests/type_declarations/typed_properties_114.phpt b/Zend/tests/type_declarations/typed_properties_114.phpt new file mode 100644 index 00000000000..e771f4c1c1f --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_114.phpt @@ -0,0 +1,39 @@ +--TEST-- +Typed property type error through ArrayIterator +--FILE-- + &$v) { + try { + $v = []; + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } +} +foreach ($obj as $k => &$v) { + try { + $v = []; + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + } +} + +var_dump($obj); +?> +--EXPECT-- +Cannot assign array to reference held by property A::$foo of type string +Cannot assign array to reference held by property A::$foo of type string +object(A)#1 (1) { + ["foo"]=> + &string(3) "bar" +} diff --git a/Zend/tests/type_declarations/typed_properties_115.phpt b/Zend/tests/type_declarations/typed_properties_115.phpt new file mode 100644 index 00000000000..eb96b3ee886 --- /dev/null +++ b/Zend/tests/type_declarations/typed_properties_115.phpt @@ -0,0 +1,30 @@ +--TEST-- +Readonly property modification error through ArrayIterator +--FILE-- + &$v) {} +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} + +var_dump($obj); +?> +--EXPECT-- +Cannot acquire reference to readonly property A::$foo +object(A)#1 (1) { + ["foo"]=> + string(3) "bar" +} diff --git a/ext/spl/spl_array.c b/ext/spl/spl_array.c index 79f6c5016c6..9997e6c575d 100644 --- a/ext/spl/spl_array.c +++ b/ext/spl/spl_array.c @@ -56,6 +56,11 @@ typedef struct _spl_array_object { zend_object std; } spl_array_object; +typedef struct _spl_array_iterator { + zend_object_iterator it; + bool by_ref; +} spl_array_iterator; + static inline spl_array_object *spl_array_from_obj(zend_object *obj) /* {{{ */ { return (spl_array_object*)((char*)(obj) - XtOffsetOf(spl_array_object, std)); } @@ -978,12 +983,34 @@ static int spl_array_it_valid(zend_object_iterator *iter) /* {{{ */ static zval *spl_array_it_get_current_data(zend_object_iterator *iter) /* {{{ */ { + spl_array_iterator *array_iter = (spl_array_iterator*)iter; spl_array_object *object = Z_SPLARRAY_P(&iter->data); HashTable *aht = spl_array_get_hash_table(object); zval *data = zend_hash_get_current_data_ex(aht, spl_array_get_pos_ptr(aht, object)); if (data && Z_TYPE_P(data) == IS_INDIRECT) { data = Z_INDIRECT_P(data); } + // ZEND_FE_FETCH_RW converts the value to a reference but doesn't know the source is a property. + // Typed properties must add a type source to the reference, and readonly properties must fail. + if (array_iter->by_ref + && Z_TYPE_P(data) != IS_REFERENCE + && Z_TYPE(object->array) == IS_OBJECT + && !(object->ar_flags & (SPL_ARRAY_IS_SELF|SPL_ARRAY_USE_OTHER))) { + zend_string *key; + zend_hash_get_current_key_ex(aht, &key, NULL, spl_array_get_pos_ptr(aht, object)); + zend_class_entry *ce = Z_OBJCE(object->array); + zend_property_info *prop_info = zend_get_property_info(ce, key, true); + if (ZEND_TYPE_IS_SET(prop_info->type)) { + if (prop_info->flags & ZEND_ACC_READONLY) { + zend_throw_error(NULL, + "Cannot acquire reference to readonly property %s::$%s", + ZSTR_VAL(prop_info->ce->name), ZSTR_VAL(key)); + return NULL; + } + ZVAL_NEW_REF(data, data); + ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), prop_info); + } + } return data; } /* }}} */ @@ -1103,13 +1130,14 @@ static const zend_object_iterator_funcs spl_array_it_funcs = { static zend_object_iterator *spl_array_get_iterator(zend_class_entry *ce, zval *object, int by_ref) /* {{{ */ { - zend_object_iterator *iterator = emalloc(sizeof(zend_object_iterator)); - zend_iterator_init(iterator); + spl_array_iterator *iterator = emalloc(sizeof(spl_array_iterator)); + zend_iterator_init(&iterator->it); - ZVAL_OBJ_COPY(&iterator->data, Z_OBJ_P(object)); - iterator->funcs = &spl_array_it_funcs; + ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object)); + iterator->it.funcs = &spl_array_it_funcs; + iterator->by_ref = by_ref; - return iterator; + return &iterator->it; } /* }}} */