Skip to content
Open
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
33 changes: 33 additions & 0 deletions Zend/tests/lazy_objects/gh20174.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
--TEST--
GH-20174: Assertion failure in ReflectionProperty::skipLazyInitialization after failed LazyProxy skipLazyInitialization
--CREDITS--
vi3tL0u1s
--FILE--
<?php

class C {
public $b;
public $c;
}

$reflector = new ReflectionClass(C::class);

$obj = $reflector->newLazyProxy(function ($obj) {
$obj->b = 4;
throw new Exception();
});

try {
$reflector->initializeLazyObject($obj);
} catch (Exception $e) {
$reflector->getProperty('b')->skipLazyInitialization($obj);
}

var_dump($obj);

?>
--EXPECTF--
lazy proxy object(C)#%d (1) {
["b"]=>
NULL
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});

// Initializer effects on the proxy are not reverted
test('Proxy', $obj);

--EXPECTF--
Expand All @@ -63,12 +62,10 @@ Is lazy: 1
# Proxy:
string(11) "initializer"
initializer exception
lazy proxy object(C)#%d (3) {
["a"]=>
int(3)
lazy proxy object(C)#%d (1) {
["b"]=>
int(4)
uninitialized(int)
["c"]=>
int(5)
int(0)
}
Is lazy: 1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});

// Initializer effects on the proxy are not reverted
test('Proxy', $obj);

--EXPECTF--
Expand All @@ -66,14 +65,10 @@ Is lazy: 1
# Proxy:
string(11) "initializer"
initializer exception
lazy proxy object(C)#%d (4) {
["a"]=>
int(3)
lazy proxy object(C)#%d (1) {
["b"]=>
int(4)
uninitialized(int)
["c"]=>
int(5)
["d"]=>
int(6)
int(0)
}
Is lazy: 1
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});

// Initializer effects on the proxy are not reverted
test('Proxy', $obj);

--EXPECTF--
Expand All @@ -73,14 +72,10 @@ array(0) {
}
string(11) "initializer"
initializer exception
lazy proxy object(C)#%d (4) {
["a"]=>
int(3)
lazy proxy object(C)#%d (1) {
["b"]=>
int(4)
uninitialized(int)
["c"]=>
int(5)
["d"]=>
int(6)
int(0)
}
Is lazy: 1
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});

// Initializer effects on the proxy are not reverted
test('Proxy', $obj);

--EXPECTF--
Expand All @@ -74,12 +73,10 @@ array(1) {
}
string(11) "initializer"
initializer exception
lazy proxy object(C)#%d (3) {
["a"]=>
int(3)
lazy proxy object(C)#%d (1) {
["b"]=>
int(4)
uninitialized(int)
["c"]=>
int(5)
int(0)
}
Is lazy: 1
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ $obj = $reflector->newLazyProxy(function ($obj) {
throw new Exception('initializer exception');
});

// Initializer effects on the proxy are not reverted
test('Proxy', $obj);

--EXPECTF--
Expand Down Expand Up @@ -83,13 +82,11 @@ array(1) {
}
string(11) "initializer"
initializer exception
lazy proxy object(C)#%d (3) {
["a"]=>
int(3)
lazy proxy object(C)#%d (1) {
["b"]=>
int(4)
uninitialized(int)
["c"]=>
int(5)
int(0)
}
Is lazy: 1

Expand Down
50 changes: 42 additions & 8 deletions Zend/zend_lazy_objects.c
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,24 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj)
/* prevent reentrant initialization */
OBJ_EXTRA_FLAGS(obj) &= ~(IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY);

zval *properties_table_snapshot = NULL;

/* Snapshot dynamic properties */
HashTable *properties_snapshot = obj->properties;
if (properties_snapshot) {
GC_TRY_ADDREF(properties_snapshot);
}

/* Snapshot declared properties */
if (obj->ce->default_properties_count) {
zval *properties_table = obj->properties_table;
properties_table_snapshot = emalloc(sizeof(*properties_table_snapshot) * obj->ce->default_properties_count);

for (int i = 0; i < obj->ce->default_properties_count; i++) {
ZVAL_COPY_PROP(&properties_table_snapshot[i], &properties_table[i]);
}
}

/* Call factory */
zval retval;
int argc = 1;
Expand All @@ -492,33 +510,29 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj)
zend_call_known_fcc(initializer, &retval, argc, &zobj, named_params);

if (UNEXPECTED(EG(exception))) {
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY;
goto exit;
goto fail;
}

if (UNEXPECTED(Z_TYPE(retval) != IS_OBJECT)) {
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY;
zend_type_error("Lazy proxy factory must return an instance of a class compatible with %s, %s returned",
ZSTR_VAL(obj->ce->name),
zend_zval_value_name(&retval));
zval_ptr_dtor(&retval);
goto exit;
goto fail;
}

if (UNEXPECTED(Z_TYPE(retval) != IS_OBJECT || !zend_lazy_object_compatible(Z_OBJ(retval), obj))) {
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY;
zend_type_error("The real instance class %s is not compatible with the proxy class %s. The proxy must be a instance of the same class as the real instance, or a sub-class with no additional properties, and no overrides of the __destructor or __clone methods.",
zend_zval_value_name(&retval),
ZSTR_VAL(obj->ce->name));
zval_ptr_dtor(&retval);
goto exit;
goto fail;
}

if (UNEXPECTED(Z_OBJ(retval) == obj || zend_object_is_lazy(Z_OBJ(retval)))) {
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY;
zend_throw_error(NULL, "Lazy proxy factory must return a non-lazy object");
zval_ptr_dtor(&retval);
goto exit;
goto fail;
}

zend_fcc_dtor(&info->u.initializer.fcc);
Expand All @@ -542,6 +556,21 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj)
}
}

if (properties_table_snapshot) {
for (int i = 0; i < obj->ce->default_properties_count; i++) {
zval *p = &properties_table_snapshot[i];
/* Use zval_ptr_dtor directly here (not zend_object_dtor_property),
* as any reference type_source will have already been deleted in
* case the prop is not bound to this value anymore. */
i_zval_ptr_dtor(p);
}
efree(properties_table_snapshot);
}

if (properties_snapshot) {
zend_release_properties(properties_snapshot);
}

instance = Z_OBJ(retval);

exit:
Expand All @@ -554,6 +583,11 @@ static zend_object *zend_lazy_object_init_proxy(zend_object *obj)
}

return instance;

fail:
OBJ_EXTRA_FLAGS(obj) |= IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY;
zend_lazy_object_revert_init(obj, properties_table_snapshot, properties_snapshot);
goto exit;
}

/* Initialize a lazy object. */
Expand Down