diff --git a/Zend/tests/gh19053.phpt b/Zend/tests/gh19053.phpt new file mode 100644 index 0000000000000..7c82f1bc5f412 --- /dev/null +++ b/Zend/tests/gh19053.phpt @@ -0,0 +1,26 @@ +--TEST-- +GH-19053: Incorrect properties_info_table for abstract properties +--FILE-- + 2; } +} + +$c = new C; +var_dump($c); + +?> +--EXPECTF-- +object(C)#%d (0) { + ["foo"]=> + uninitialized(mixed) +} diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 6f399fbfec5ab..ced75ad82d9db 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1687,10 +1687,25 @@ void zend_build_properties_info_table(zend_class_entry *ce) } } - ZEND_HASH_MAP_FOREACH_PTR(&ce->properties_info, prop) { + ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->properties_info, zend_string *key, prop) { if (prop->ce == ce && (prop->flags & ZEND_ACC_STATIC) == 0 && !(prop->flags & ZEND_ACC_VIRTUAL)) { - uint32_t prop_table_offset = OBJ_PROP_TO_NUM(!(prop->prototype->flags & ZEND_ACC_VIRTUAL) ? prop->prototype->offset : prop->offset); + const zend_property_info *root_prop = prop->prototype; + if (UNEXPECTED(root_prop->flags & ZEND_ACC_VIRTUAL)) { + /* Prototype is virtual, we need to manually hunt down the first backed property. */ + root_prop = prop; + zend_class_entry *parent_ce; + while ((parent_ce = root_prop->ce->parent)) { + zend_property_info *parent_prop = zend_hash_find_ptr(&parent_ce->properties_info, key); + if (!parent_prop + || parent_prop->prototype != prop->prototype + || (parent_prop->flags & ZEND_ACC_VIRTUAL)) { + break; + } + root_prop = parent_prop; + } + } + uint32_t prop_table_offset = OBJ_PROP_TO_NUM(root_prop->offset); table[prop_table_offset] = prop; } } ZEND_HASH_FOREACH_END();