diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 462e567f3f36..60151a2c0a6f 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -129,6 +129,9 @@ PHP 8.6 INTERNALS UPGRADE NOTES zend_reflection_property_set_raw_value() to expose the functionality of ReflectionProperty::setRawValueWithoutLazyInitialization() and ReflectionProperty::setRawValue() to C extensions. + . Added zend_object_set_properties_reinitable() to centralise temporarily + allowing reinitialisation of initialised readonly properties during + controlled operations such as cloning and unserialisation. ======================== 2. Build system changes diff --git a/Zend/zend_objects.c b/Zend/zend_objects.c index 474157e73d39..03ac5de67c78 100644 --- a/Zend/zend_objects.c +++ b/Zend/zend_objects.c @@ -193,6 +193,24 @@ ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce) return object; } +ZEND_API void ZEND_FASTCALL zend_object_set_properties_reinitable(zend_object *object, bool reinitable) +{ + if (!ZEND_CLASS_HAS_READONLY_PROPS(object->ce)) { + return; + } + + for (uint32_t i = 0; i < object->ce->default_properties_count; i++) { + zval *prop = OBJ_PROP_NUM(object, i); + if (reinitable) { + if (!Z_ISUNDEF_P(prop)) { + Z_PROP_FLAG_P(prop) |= IS_PROP_REINITABLE; + } + } else { + Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; + } + } +} + ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, const zend_object *old_object) { bool has_clone_method = old_object->ce->clone != NULL; @@ -206,10 +224,6 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, i_zval_ptr_dtor(dst); ZVAL_COPY_VALUE_PROP(dst, src); zval_add_ref(dst); - if (has_clone_method) { - /* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */ - Z_PROP_FLAG_P(dst) |= IS_PROP_REINITABLE; - } if (UNEXPECTED(Z_ISREF_P(dst)) && (ZEND_DEBUG || ZEND_REF_HAS_TYPE_SOURCES(Z_REF_P(dst)))) { @@ -255,10 +269,7 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, ZVAL_COPY_VALUE(&new_prop, prop); zval_add_ref(&new_prop); } - if (has_clone_method) { - /* Unconditionally add the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */ - Z_PROP_FLAG_P(&new_prop) |= IS_PROP_REINITABLE; - } + if (EXPECTED(key)) { _zend_hash_append(new_object->properties, key, &new_prop); } else { @@ -268,15 +279,9 @@ ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, } if (has_clone_method) { + zend_object_set_properties_reinitable(new_object, /* reinitable */ true); zend_call_known_instance_method_with_0_params(new_object->ce->clone, new_object, NULL); - - if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { - for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { - zval* prop = OBJ_PROP_NUM(new_object, i); - /* Unconditionally remove the IS_PROP_REINITABLE flag to avoid a potential cache miss of property_info */ - Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; - } - } + zend_object_set_properties_reinitable(new_object, /* reinitable */ false); } } @@ -285,13 +290,8 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const zend_object *new_object = old_object->handlers->clone_obj(old_object); if (EXPECTED(!EG(exception))) { - /* Unlock readonly properties once more. */ - if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { - for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { - zval* prop = OBJ_PROP_NUM(new_object, i); - Z_PROP_FLAG_P(prop) |= IS_PROP_REINITABLE; - } - } + + zend_object_set_properties_reinitable(new_object, /* reinitable */ true); const zend_class_entry *old_scope = EG(fake_scope); @@ -322,13 +322,7 @@ ZEND_API zend_object *zend_objects_clone_obj_with(zend_object *old_object, const EG(fake_scope) = old_scope; - /* Lock readonly properties once more. */ - if (ZEND_CLASS_HAS_READONLY_PROPS(new_object->ce)) { - for (uint32_t i = 0; i < new_object->ce->default_properties_count; i++) { - zval* prop = OBJ_PROP_NUM(new_object, i); - Z_PROP_FLAG_P(prop) &= ~IS_PROP_REINITABLE; - } - } + zend_object_set_properties_reinitable(new_object, /* reinitable */ false); } return new_object; diff --git a/Zend/zend_objects.h b/Zend/zend_objects.h index 0930fa043101..8bb6c053b0c7 100644 --- a/Zend/zend_objects.h +++ b/Zend/zend_objects.h @@ -25,6 +25,7 @@ BEGIN_EXTERN_C() ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce); ZEND_API zend_object* ZEND_FASTCALL zend_objects_new(zend_class_entry *ce); ZEND_API void ZEND_FASTCALL zend_objects_clone_members(zend_object *new_object, const zend_object *old_object); +ZEND_API void ZEND_FASTCALL zend_object_set_properties_reinitable(zend_object *object, bool reinitable); ZEND_API void zend_object_std_dtor(zend_object *object); ZEND_API void zend_objects_destroy_object(zend_object *object);