Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions Zend/tests/lazy_objects/gh20657-002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
27 changes: 10 additions & 17 deletions Zend/zend_lazy_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -116,15 +112,15 @@ 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));

zend_lazy_object_info *info = zend_lazy_object_get_info(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)
Expand All @@ -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)
Expand Down Expand Up @@ -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)));
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions Zend/zend_lazy_objects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}
/* }}} */

Expand Down
Loading