diff --git a/Zend/tests/lazy_objects/gh20657-002.phpt b/Zend/tests/lazy_objects/gh20657-002.phpt index daf9b767ba97..c9933c3c0eb6 100644 --- a/Zend/tests/lazy_objects/gh20657-002.phpt +++ b/Zend/tests/lazy_objects/gh20657-002.phpt @@ -14,30 +14,38 @@ class D { } public function __destruct() { global $obj, $reflector; + echo __METHOD__, "\n"; $reflector->resetAsLazyGhost($obj, function () {}); } } +// A cycle, dtor is called on next GC new D(); $reflector = new ReflectionClass(C::class); -for ($i = 0; $i < 10000; $i++) { - $obj = $reflector->newLazyGhost(function ($obj) {}); +$obj = $reflector->newLazyGhost(new class { + function __invoke () {} + function __destruct() { + gc_collect_cycles(); + } +}); - // Add to roots - $obj2 = $obj; - unset($obj2); +// Add to roots +$obj2 = $obj; +unset($obj2); - // Initialize all props to mark object non-lazy. Also create a cycle. - $reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, $obj); -} +// Initialize all props to mark object non-lazy. Also create a cycle. +echo "Will realize lazy object\n"; +$reflector->getProperty('a')->setRawValueWithoutLazyInitialization($obj, $obj); +echo "Realized lazy object\n"; var_dump($obj); ?> --EXPECTF-- -object(C)#%d (1) { - ["a"]=> - *RECURSION* +Will realize lazy object +D::__destruct +Realized lazy object +lazy ghost object(C)#%d (0) { } diff --git a/Zend/zend_lazy_objects.c b/Zend/zend_lazy_objects.c index 5492419d8c67..de9538bbb4c4 100644 --- a/Zend/zend_lazy_objects.c +++ b/Zend/zend_lazy_objects.c @@ -55,10 +55,7 @@ */ typedef struct _zend_lazy_object_info { union { - struct { - zend_fcall_info_cache fcc; - zval zv; /* ReflectionClass::getLazyInitializer() */ - } initializer; + zend_fcall_info_cache initializer; zend_object *instance; /* For initialized lazy proxy objects */ } u; zend_lazy_object_flags_t flags; @@ -74,8 +71,7 @@ static void zend_lazy_object_info_dtor_func(zval *pElement) ZEND_ASSERT(info->flags & ZEND_LAZY_OBJECT_STRATEGY_PROXY); zend_object_release(info->u.instance); } else { - zval_ptr_dtor(&info->u.initializer.zv); - zend_fcc_dtor(&info->u.initializer.fcc); + zend_fcc_dtor(&info->u.initializer); } efree(info); @@ -116,7 +112,7 @@ static bool zend_lazy_object_has_stale_info(const zend_object *obj) return zend_hash_index_find_ptr(&EG(lazy_objects_store).infos, obj->handle); } -zval* zend_lazy_object_get_initializer_zv(zend_object *obj) +ZEND_API void zend_lazy_object_get_initializer_callback(zend_object *obj, zval *callback) { ZEND_ASSERT(!zend_lazy_object_initialized(obj)); @@ -124,7 +120,7 @@ zval* zend_lazy_object_get_initializer_zv(zend_object *obj) ZEND_ASSERT(!(info->flags & ZEND_LAZY_OBJECT_INITIALIZED)); - return &info->u.initializer.zv; + zend_get_callable_zval_from_fcc(&info->u.initializer, callback); } static zend_fcall_info_cache* zend_lazy_object_get_initializer_fcc(const zend_object *obj) @@ -135,7 +131,7 @@ static zend_fcall_info_cache* zend_lazy_object_get_initializer_fcc(const zend_ob ZEND_ASSERT(!(info->flags & ZEND_LAZY_OBJECT_INITIALIZED)); - return &info->u.initializer.fcc; + return &info->u.initializer; } zend_object* zend_lazy_object_get_instance(zend_object *obj) @@ -236,7 +232,7 @@ static bool zlo_is_iterating(zend_object *object) /* Make object 'obj' lazy. If 'obj' is NULL, create a lazy instance of * class 'reflection_ce' */ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, - zend_class_entry *reflection_ce, const zval *initializer_zv, + zend_class_entry *reflection_ce, const zend_fcall_info_cache *initializer_fcc, zend_lazy_object_flags_t flags) { ZEND_ASSERT(!(flags & ~(ZEND_LAZY_OBJECT_USER_MASK|ZEND_LAZY_OBJECT_STRATEGY_MASK))); @@ -372,8 +368,7 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, } zend_lazy_object_info *info = emalloc(sizeof(*info)); - zend_fcc_dup(&info->u.initializer.fcc, initializer_fcc); - ZVAL_COPY(&info->u.initializer.zv, initializer_zv); + zend_fcc_dup(&info->u.initializer, initializer_fcc); info->flags = flags; info->lazy_properties_count = lazy_properties_count; zend_lazy_object_set_info(obj, info); @@ -514,7 +509,7 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj) zval retval; zval zobj; HashTable *named_params = NULL; - const zend_fcall_info_cache *initializer = &info->u.initializer.fcc; + const zend_fcall_info_cache *initializer = &info->u.initializer; zend_object *instance = NULL; ZVAL_OBJ(&zobj, obj); @@ -547,8 +542,7 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj) goto fail; } - zend_fcc_dtor(&info->u.initializer.fcc); - zval_ptr_dtor(&info->u.initializer.zv); + zend_fcc_dtor(&info->u.initializer); info->u.instance = Z_OBJ(retval); info->flags |= ZEND_LAZY_OBJECT_INITIALIZED; OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_PROXY; @@ -835,8 +829,7 @@ HashTable *zend_lazy_object_get_gc(zend_object *zobj, zval **table, int *n) return NULL; } - zend_get_gc_buffer_add_fcc(gc_buffer, &info->u.initializer.fcc); - zend_get_gc_buffer_add_zval(gc_buffer, &info->u.initializer.zv); + zend_get_gc_buffer_add_fcc(gc_buffer, &info->u.initializer); /* Lazy objects may have a properties ht in two cases: * - After fetching debug infos diff --git a/Zend/zend_lazy_objects.h b/Zend/zend_lazy_objects.h index f517d9b31a51..aea926782886 100644 --- a/Zend/zend_lazy_objects.h +++ b/Zend/zend_lazy_objects.h @@ -58,14 +58,14 @@ typedef struct _zend_fcall_info_cache zend_fcall_info_cache; ZEND_API bool zend_class_can_be_lazy(const zend_class_entry *ce); ZEND_API zend_object *zend_object_make_lazy(zend_object *obj, - zend_class_entry *reflection_ce, const zval *initializer_zv, + zend_class_entry *reflection_ce, const zend_fcall_info_cache *initializer_fcc, zend_lazy_object_flags_t flags); ZEND_API zend_object *zend_lazy_object_init(zend_object *obj); ZEND_API zend_object *zend_lazy_object_mark_as_initialized(zend_object *obj); void zend_lazy_objects_init(zend_lazy_objects_store *store); void zend_lazy_objects_destroy(zend_lazy_objects_store *store); -zval* zend_lazy_object_get_initializer_zv(zend_object *obj); +ZEND_API void zend_lazy_object_get_initializer_callback(zend_object *obj, zval *callback); zend_object *zend_lazy_object_get_instance(zend_object *obj); zend_lazy_object_flags_t zend_lazy_object_get_flags(const zend_object *obj); void zend_lazy_object_del_info(const zend_object *obj); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index c4341e281430..8b07277d4837 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -5145,7 +5145,7 @@ static zend_always_inline void reflection_class_new_lazy(INTERNAL_FUNCTION_PARAM zend_is_callable(&fci.function_name, &fcc, NULL); } - obj = zend_object_make_lazy(obj, ce, &fci.function_name, &fcc, + obj = zend_object_make_lazy(obj, ce, &fcc, strategy | options); if (!obj) { @@ -5277,7 +5277,7 @@ ZEND_METHOD(ReflectionClass, getLazyInitializer) RETURN_NULL(); } - RETURN_ZVAL(zend_lazy_object_get_initializer_zv(object), true, false); + zend_lazy_object_get_initializer_callback(object, return_value); } /* }}} */