mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Fix lazy object foreach edge cases
- Fix foreach on a object whose initialization failed before - Do not allow to reset an object during iteration
This commit is contained in:
parent
5cc8db8c4b
commit
720ed56929
108
Zend/tests/lazy_objects/foreach_reset.phpt
Normal file
108
Zend/tests/lazy_objects/foreach_reset.phpt
Normal file
@ -0,0 +1,108 @@
|
||||
--TEST--
|
||||
Lazy Objects: resetAsLazy() during foreach() is not allowed
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
class NoHooks {
|
||||
public int $a = 1;
|
||||
}
|
||||
|
||||
class Hooks {
|
||||
public int $a = 1;
|
||||
public int $b { get { return 2; } }
|
||||
}
|
||||
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s\n", $name);
|
||||
$reflector = new ReflectionClass($obj::class);
|
||||
foreach ($obj as $key => $value) {
|
||||
var_dump($key);
|
||||
try {
|
||||
$reflector->resetAsLazyProxy($obj, function () {});
|
||||
} catch (Error $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$nohooks = new ReflectionClass(NoHooks::class);
|
||||
$hooks = new ReflectionClass(Hooks::class);
|
||||
|
||||
$obj = $nohooks->newLazyGhost(function () {});
|
||||
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $hooks->newLazyGhost(function () {});
|
||||
|
||||
test('Ghost (hooks)', $obj);
|
||||
|
||||
$obj = $nohooks->newLazyProxy(function () {
|
||||
return new NoHooks();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
$obj = $hooks->newLazyProxy(function () {
|
||||
return new Hooks();
|
||||
});
|
||||
|
||||
test('Proxy', $obj);
|
||||
|
||||
$obj = new NoHooks();
|
||||
|
||||
test('Non lazy', $obj);
|
||||
|
||||
$obj = new Hooks();
|
||||
|
||||
test('Non lazy (hooks)', $obj);
|
||||
|
||||
$obj = new NoHooks();
|
||||
ob_start();
|
||||
var_dump($obj);
|
||||
ob_end_clean();
|
||||
|
||||
test('Non lazy with ht', $obj);
|
||||
|
||||
$obj = new Hooks();
|
||||
ob_start();
|
||||
var_dump($obj);
|
||||
ob_end_clean();
|
||||
|
||||
test('Non lazy with ht (hooks)', $obj);
|
||||
|
||||
?>
|
||||
==DONE==
|
||||
--EXPECT--
|
||||
# Ghost
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Ghost (hooks)
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
string(1) "b"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Proxy
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Proxy
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
string(1) "b"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Non lazy
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Non lazy (hooks)
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
string(1) "b"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Non lazy with ht
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
# Non lazy with ht (hooks)
|
||||
string(1) "a"
|
||||
Error: Can not reset an object during property iteration
|
||||
string(1) "b"
|
||||
Error: Can not reset an object during property iteration
|
||||
==DONE==
|
@ -5,9 +5,11 @@ Lazy objects: Foreach initializes object
|
||||
|
||||
class C {
|
||||
public int $a;
|
||||
public int $b;
|
||||
public function __construct() {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +26,17 @@ foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Ghost (by ref):\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => &$value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Proxy:\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
@ -35,14 +48,110 @@ foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
--EXPECTF--
|
||||
print "# Proxy (by ref):\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => &$value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Ghost (init failure)\n";
|
||||
|
||||
$fail = true;
|
||||
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
|
||||
if ($fail) {
|
||||
throw new Exception("initializer");
|
||||
} else {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
$fail = false;
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Ghost (init failure, by ref)\n";
|
||||
|
||||
$fail = true;
|
||||
$obj = $reflector->newLazyGhost(function ($obj) use (&$fail) {
|
||||
if ($fail) {
|
||||
throw new Exception("initializer");
|
||||
} else {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
foreach ($obj as $prop => &$value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
printf("%s: %s\n", $e::class, $e->getMessage());
|
||||
}
|
||||
|
||||
$fail = false;
|
||||
foreach ($obj as $prop => &$value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
# Ghost:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Ghost (by ref):
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Proxy:
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Proxy (by ref):
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Ghost (init failure)
|
||||
Exception: initializer
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Ghost (init failure, by ref)
|
||||
Exception: initializer
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
|
@ -6,49 +6,79 @@ Lazy objects: Foreach initializes object
|
||||
#[AllowDynamicProperties]
|
||||
class C {
|
||||
public int $a;
|
||||
private int $_b;
|
||||
public int $b {
|
||||
get { return $this->b; }
|
||||
set(int $value) { $this->b = $value; }
|
||||
&get { $ref = &$this->_b; return $ref; }
|
||||
}
|
||||
public int $c {
|
||||
get { return $this->a + 2; }
|
||||
}
|
||||
public function __construct() {
|
||||
public function __construct(bool $addDynamic = true) {
|
||||
var_dump(__METHOD__);
|
||||
$this->a = 1;
|
||||
$this->b = 2;
|
||||
$this->d = 4;
|
||||
$this->_b = 2;
|
||||
if ($addDynamic) {
|
||||
$this->c = 3;
|
||||
$this->d = 4;
|
||||
unset($this->c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$reflector = new ReflectionClass(C::class);
|
||||
|
||||
print "# Ghost:\n";
|
||||
function test(string $name, object $obj) {
|
||||
printf("# %s:\n", $name);
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
foreach ($obj as $prop => &$value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
|
||||
print "# Proxy:\n";
|
||||
test('Ghost', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C();
|
||||
});
|
||||
|
||||
foreach ($obj as $prop => $value) {
|
||||
var_dump($prop, $value);
|
||||
}
|
||||
test('Proxy', $obj);
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
var_dump("initializer");
|
||||
$obj->__construct(addDynamic: false);
|
||||
});
|
||||
|
||||
test('Ghost (no dynamic)', $obj);
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
var_dump("initializer");
|
||||
return new C(addDynamic: false);
|
||||
});
|
||||
|
||||
test('Proxy (no dynamic)', $obj);
|
||||
|
||||
print "# Proxy of proxy (initialization)\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) use (&$obj2, $reflector) {
|
||||
var_dump("initializer");
|
||||
return $obj2 = new C();
|
||||
});
|
||||
$reflector->initializeLazyObject($obj);
|
||||
$reflector->resetAsLazyProxy($obj2, function () {
|
||||
return new C();
|
||||
});
|
||||
|
||||
test('Proxy of proxy', $obj);
|
||||
|
||||
print "# Ghost (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyGhost(function ($obj) {
|
||||
throw new \Exception();
|
||||
throw new \Exception("initializer");
|
||||
});
|
||||
|
||||
try {
|
||||
@ -60,7 +90,7 @@ try {
|
||||
print "# Proxy (init exception):\n";
|
||||
|
||||
$obj = $reflector->newLazyProxy(function ($obj) {
|
||||
throw new \Exception();
|
||||
throw new \Exception("initializer");
|
||||
});
|
||||
|
||||
try {
|
||||
@ -77,8 +107,12 @@ string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "c"
|
||||
int(3)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
# Proxy:
|
||||
@ -88,9 +122,54 @@ string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "c"
|
||||
int(3)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
# Ghost (no dynamic):
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Proxy (no dynamic):
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
# Proxy of proxy (initialization)
|
||||
string(11) "initializer"
|
||||
string(14) "C::__construct"
|
||||
# Proxy of proxy:
|
||||
string(14) "C::__construct"
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
string(1) "a"
|
||||
int(1)
|
||||
string(1) "b"
|
||||
int(2)
|
||||
string(1) "d"
|
||||
int(4)
|
||||
# Ghost (init exception):
|
||||
Exception:
|
||||
Exception: initializer
|
||||
# Proxy (init exception):
|
||||
Exception:
|
||||
Exception: initializer
|
||||
|
@ -47,7 +47,7 @@ var_dump($obj);
|
||||
--EXPECTF--
|
||||
# Ghost:
|
||||
string(18) "Canary::__destruct"
|
||||
lazy ghost object(C)#%d (0) {
|
||||
lazy ghost object(C)#%d (%d) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(Canary)#%d (0) {
|
||||
@ -62,7 +62,7 @@ object(C)#%d (2) {
|
||||
# Proxy:
|
||||
string(18) "Canary::__destruct"
|
||||
string(18) "Canary::__destruct"
|
||||
lazy proxy object(C)#%d (0) {
|
||||
lazy proxy object(C)#%d (%d) {
|
||||
}
|
||||
string(11) "initializer"
|
||||
object(Canary)#%d (0) {
|
||||
|
@ -23,7 +23,9 @@ class ByRef {
|
||||
}
|
||||
}
|
||||
public function __construct() {
|
||||
$this->undef = 'dynamic';
|
||||
$this->dynamic = 'dynamic';
|
||||
unset($this->undef);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,6 +199,27 @@ ZEND_API bool zend_class_can_be_lazy(zend_class_entry *ce)
|
||||
return true;
|
||||
}
|
||||
|
||||
static int zlo_hash_remove_dyn_props_func(zval *pDest)
|
||||
{
|
||||
if (Z_TYPE_P(pDest) == IS_INDIRECT) {
|
||||
return ZEND_HASH_APPLY_STOP;
|
||||
}
|
||||
|
||||
return ZEND_HASH_APPLY_REMOVE;
|
||||
}
|
||||
|
||||
static bool zlo_is_iterating(zend_object *object)
|
||||
{
|
||||
if (object->properties && object->properties->u.v.nIteratorsCount) {
|
||||
return true;
|
||||
}
|
||||
if (zend_object_is_lazy_proxy(object)
|
||||
&& zend_lazy_object_initialized(object)) {
|
||||
return zlo_is_iterating(zend_lazy_object_get_instance(object));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
@ -251,6 +272,10 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (zlo_is_iterating(obj)) {
|
||||
zend_throw_error(NULL, "Can not reset an object during property iteration");
|
||||
return NULL;
|
||||
}
|
||||
if (zend_object_is_lazy(obj)) {
|
||||
ZEND_ASSERT(zend_object_is_lazy_proxy(obj) && zend_lazy_object_initialized(obj));
|
||||
OBJ_EXTRA_FLAGS(obj) &= ~(IS_OBJ_LAZY_UNINITIALIZED|IS_OBJ_LAZY_PROXY);
|
||||
@ -278,9 +303,17 @@ ZEND_API zend_object *zend_object_make_lazy(zend_object *obj,
|
||||
|
||||
GC_DEL_FLAGS(obj, IS_OBJ_DESTRUCTOR_CALLED);
|
||||
|
||||
/* unset() dynamic properties */
|
||||
zend_object_dtor_dynamic_properties(obj);
|
||||
obj->properties = NULL;
|
||||
/* unset() dynamic properties. Do not NULL out obj->properties, as this
|
||||
* would be unexpected. */
|
||||
if (obj->properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(obj->properties) > 1)) {
|
||||
if (EXPECTED(!(GC_FLAGS(obj->properties) & IS_ARRAY_IMMUTABLE))) {
|
||||
GC_DELREF(obj->properties);
|
||||
}
|
||||
obj->properties = zend_array_dup(obj->properties);
|
||||
}
|
||||
zend_hash_reverse_apply(obj->properties, zlo_hash_remove_dyn_props_func);
|
||||
}
|
||||
|
||||
/* unset() declared properties */
|
||||
for (int i = 0; i < reflection_ce->default_properties_count; i++) {
|
||||
@ -513,6 +546,9 @@ ZEND_API zend_object *zend_lazy_object_init(zend_object *obj)
|
||||
ZEND_ASSERT(zend_object_is_lazy_proxy(obj));
|
||||
zend_lazy_object_info *info = zend_lazy_object_get_info(obj);
|
||||
ZEND_ASSERT(info->flags & ZEND_LAZY_OBJECT_INITIALIZED);
|
||||
if (zend_object_is_lazy(info->u.instance)) {
|
||||
return zend_lazy_object_init(info->u.instance);
|
||||
}
|
||||
return info->u.instance;
|
||||
}
|
||||
|
||||
|
@ -51,13 +51,6 @@ static uint32_t zho_num_backed_props(zend_object *zobj)
|
||||
|
||||
static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access, bool include_dynamic_props)
|
||||
{
|
||||
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(!zobj)) {
|
||||
return zend_new_array(0);
|
||||
}
|
||||
}
|
||||
|
||||
zend_class_entry *ce = zobj->ce;
|
||||
zend_array *properties = zend_new_array(ce->default_properties_count);
|
||||
zend_hash_real_init_mixed(properties);
|
||||
@ -93,23 +86,22 @@ static zend_array *zho_build_properties_ex(zend_object *zobj, bool check_access,
|
||||
|
||||
ZEND_API zend_array *zend_hooked_object_build_properties(zend_object *zobj)
|
||||
{
|
||||
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(!zobj)) {
|
||||
return (zend_array*) &zend_empty_array;
|
||||
}
|
||||
}
|
||||
|
||||
return zho_build_properties_ex(zobj, false, true);
|
||||
}
|
||||
|
||||
static bool zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
|
||||
static void zho_dynamic_it_init(zend_hooked_object_iterator *hooked_iter)
|
||||
{
|
||||
if (hooked_iter->dynamic_prop_it != (uint32_t) -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
zend_object *zobj = Z_OBJ_P(&hooked_iter->it.data);
|
||||
if (!zobj->properties || zho_num_backed_props(zobj) == zobj->properties->nNumUsed) {
|
||||
hooked_iter->dynamic_props_done = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
hooked_iter->dynamic_prop_it = zend_hash_iterator_add(zobj->properties, zho_num_backed_props(zobj));
|
||||
return true;
|
||||
zend_array *properties = zobj->handlers->get_properties(zobj);
|
||||
hooked_iter->dynamic_props_done = false;
|
||||
hooked_iter->dynamic_prop_it = zend_hash_iterator_add(properties, zho_num_backed_props(zobj));
|
||||
}
|
||||
|
||||
static void zho_it_get_current_key(zend_object_iterator *iter, zval *key);
|
||||
@ -168,7 +160,17 @@ static void zho_dynamic_it_fetch_current(zend_object_iterator *iter)
|
||||
zend_array *properties = Z_OBJ(iter->data)->properties;
|
||||
HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
|
||||
|
||||
if (pos >= properties->nNumUsed) {
|
||||
hooked_iter->dynamic_props_done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Bucket *bucket = properties->arData + pos;
|
||||
|
||||
if (UNEXPECTED(Z_TYPE(bucket->val) == IS_UNDEF)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hooked_iter->by_ref && Z_TYPE(bucket->val) != IS_REFERENCE) {
|
||||
ZEND_ASSERT(Z_TYPE(bucket->val) != IS_UNDEF);
|
||||
ZVAL_MAKE_REF(&bucket->val);
|
||||
@ -192,7 +194,7 @@ static void zho_it_fetch_current(zend_object_iterator *iter)
|
||||
while (true) {
|
||||
if (!hooked_iter->declared_props_done) {
|
||||
zho_declared_it_fetch_current(iter);
|
||||
} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
|
||||
} else if (!hooked_iter->dynamic_props_done) {
|
||||
zho_dynamic_it_fetch_current(iter);
|
||||
} else {
|
||||
break;
|
||||
@ -252,14 +254,11 @@ static void zho_it_move_forward(zend_object_iterator *iter)
|
||||
if (zend_hash_has_more_elements(properties) != SUCCESS) {
|
||||
hooked_iter->declared_props_done = true;
|
||||
}
|
||||
} else if (!hooked_iter->dynamic_props_done && zho_dynamic_it_init(hooked_iter)) {
|
||||
} else if (!hooked_iter->dynamic_props_done) {
|
||||
zend_array *properties = Z_OBJ(iter->data)->properties;
|
||||
HashPosition pos = zend_hash_iterator_pos(hooked_iter->dynamic_prop_it, properties);
|
||||
pos++;
|
||||
EG(ht_iterators)[hooked_iter->dynamic_prop_it].pos = pos;
|
||||
if (pos >= properties->nNumUsed) {
|
||||
hooked_iter->dynamic_props_done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -305,17 +304,24 @@ static const zend_object_iterator_funcs zend_hooked_object_it_funcs = {
|
||||
|
||||
ZEND_API zend_object_iterator *zend_hooked_object_get_iterator(zend_class_entry *ce, zval *object, int by_ref)
|
||||
{
|
||||
zend_object *zobj = Z_OBJ_P(object);
|
||||
if (UNEXPECTED(zend_lazy_object_must_init(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(!zobj)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
zend_hooked_object_iterator *iterator = emalloc(sizeof(zend_hooked_object_iterator));
|
||||
zend_iterator_init(&iterator->it);
|
||||
|
||||
ZVAL_OBJ_COPY(&iterator->it.data, Z_OBJ_P(object));
|
||||
ZVAL_OBJ_COPY(&iterator->it.data, zobj);
|
||||
iterator->it.funcs = &zend_hooked_object_it_funcs;
|
||||
iterator->by_ref = by_ref;
|
||||
iterator->declared_props_done = false;
|
||||
zend_array *properties = zho_build_properties_ex(Z_OBJ_P(object), true, false);
|
||||
zend_array *properties = zho_build_properties_ex(zobj, true, false);
|
||||
ZVAL_ARR(&iterator->declared_props, properties);
|
||||
iterator->dynamic_props_done = false;
|
||||
iterator->dynamic_prop_it = (uint32_t) -1;
|
||||
zho_dynamic_it_init(iterator);
|
||||
ZVAL_UNDEF(&iterator->current_key);
|
||||
ZVAL_UNDEF(&iterator->current_data);
|
||||
|
||||
|
@ -6821,6 +6821,14 @@ ZEND_VM_HANDLER(77, ZEND_FE_RESET_R, CONST|TMP|VAR|CV, JMP_ADDR)
|
||||
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
if (!zobj->ce->get_iterator) {
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
FREE_OP1_IF_VAR();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
HashTable *properties = zobj->properties;
|
||||
if (properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
|
||||
@ -6909,7 +6917,16 @@ ZEND_VM_COLD_CONST_HANDLER(125, ZEND_FE_RESET_RW, CONST|TMP|VAR|CV, JMP_ADDR)
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (OP1_TYPE != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
HashTable *properties;
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
FREE_OP1_IF_VAR();
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
if (OP1_TYPE == IS_VAR || OP1_TYPE == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
|
68
Zend/zend_vm_execute.h
generated
68
Zend/zend_vm_execute.h
generated
@ -5411,6 +5411,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CONST_HANDLER(
|
||||
} else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
if (!zobj->ce->get_iterator) {
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
HashTable *properties = zobj->properties;
|
||||
if (properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
|
||||
@ -5497,7 +5505,16 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (IS_CONST != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
HashTable *properties;
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
if (IS_CONST == IS_VAR || IS_CONST == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
@ -20117,6 +20134,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_TMP_HANDLER(ZE
|
||||
} else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
if (!zobj->ce->get_iterator) {
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
HashTable *properties = zobj->properties;
|
||||
if (properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
|
||||
@ -20204,7 +20229,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_TMP_HANDLER(Z
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (IS_TMP_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
HashTable *properties;
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
if (IS_TMP_VAR == IS_VAR || IS_TMP_VAR == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
@ -22769,6 +22803,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_VAR_HANDLER(ZE
|
||||
} else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
if (!zobj->ce->get_iterator) {
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
HashTable *properties = zobj->properties;
|
||||
if (properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
|
||||
@ -22857,7 +22899,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_VAR_HANDLER(Z
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (IS_VAR != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
HashTable *properties;
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
zval_ptr_dtor_nogc(EX_VAR(opline->op1.var));
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
if (IS_VAR == IS_VAR || IS_VAR == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
@ -41067,6 +41118,14 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_R_SPEC_CV_HANDLER(ZEN
|
||||
} else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
if (!zobj->ce->get_iterator) {
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
HashTable *properties = zobj->properties;
|
||||
if (properties) {
|
||||
if (UNEXPECTED(GC_REFCOUNT(properties) > 1)) {
|
||||
@ -41153,7 +41212,16 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_FE_RESET_RW_SPEC_CV_HANDLER(ZE
|
||||
ZEND_VM_NEXT_OPCODE();
|
||||
} else if (IS_CV != IS_CONST && EXPECTED(Z_TYPE_P(array_ptr) == IS_OBJECT)) {
|
||||
if (!Z_OBJCE_P(array_ptr)->get_iterator) {
|
||||
zend_object *zobj = Z_OBJ_P(array_ptr);
|
||||
HashTable *properties;
|
||||
if (UNEXPECTED(zend_object_is_lazy(zobj))) {
|
||||
zobj = zend_lazy_object_init(zobj);
|
||||
if (UNEXPECTED(EG(exception))) {
|
||||
UNDEF_RESULT();
|
||||
|
||||
HANDLE_EXCEPTION();
|
||||
}
|
||||
}
|
||||
if (IS_CV == IS_VAR || IS_CV == IS_CV) {
|
||||
if (array_ptr == array_ref) {
|
||||
ZVAL_NEW_REF(array_ref, array_ref);
|
||||
|
Loading…
Reference in New Issue
Block a user