Skip to content

Commit 4df25e1

Browse files
committed
Repeat dynamic property check after __isset for ??
Fixes GH-12695
1 parent 6f95273 commit 4df25e1

File tree

2 files changed

+62
-25
lines changed

2 files changed

+62
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Coalesce IS fetch should repeat dynamic property check after __isset
3+
--FILE--
4+
<?php
5+
#[AllowDynamicProperties]
6+
class A {
7+
public function __isset($prop) {
8+
echo __FUNCTION__, "\n";
9+
$this->$prop = 123;
10+
return true;
11+
}
12+
public function __get($prop) {
13+
throw new Exception('Unreachable');
14+
}
15+
}
16+
17+
$a = new A;
18+
echo $a->foo ?? 234;
19+
?>
20+
--EXPECT--
21+
__isset
22+
123

Zend/zend_object_handlers.c

+40-25
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,36 @@ ZEND_API uint32_t *zend_get_recursion_guard(zend_object *zobj)
604604
return &Z_GUARD_P(zv);
605605
}
606606

607+
static zval *zend_std_read_dynamic_property(zend_object *zobj, zend_string *name, void **cache_slot, uintptr_t property_offset)
608+
{
609+
if (EXPECTED(zobj->properties != NULL)) {
610+
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
611+
uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
612+
613+
if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
614+
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
615+
616+
if (EXPECTED(p->key == name) ||
617+
(EXPECTED(p->h == ZSTR_H(name)) &&
618+
EXPECTED(p->key != NULL) &&
619+
EXPECTED(zend_string_equal_content(p->key, name)))) {
620+
return &p->val;
621+
}
622+
}
623+
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
624+
}
625+
zval *retval = zend_hash_find(zobj->properties, name);
626+
if (EXPECTED(retval)) {
627+
if (cache_slot) {
628+
uintptr_t idx = (char*)retval - (char*)zobj->properties->arData;
629+
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
630+
}
631+
return retval;
632+
}
633+
}
634+
return NULL;
635+
}
636+
607637
ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int type, void **cache_slot, zval *rv) /* {{{ */
608638
{
609639
zval *retval;
@@ -655,31 +685,9 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
655685
goto uninit_error;
656686
}
657687
} else if (EXPECTED(IS_DYNAMIC_PROPERTY_OFFSET(property_offset))) {
658-
if (EXPECTED(zobj->properties != NULL)) {
659-
if (!IS_UNKNOWN_DYNAMIC_PROPERTY_OFFSET(property_offset)) {
660-
uintptr_t idx = ZEND_DECODE_DYN_PROP_OFFSET(property_offset);
661-
662-
if (EXPECTED(idx < zobj->properties->nNumUsed * sizeof(Bucket))) {
663-
Bucket *p = (Bucket*)((char*)zobj->properties->arData + idx);
664-
665-
if (EXPECTED(p->key == name) ||
666-
(EXPECTED(p->h == ZSTR_H(name)) &&
667-
EXPECTED(p->key != NULL) &&
668-
EXPECTED(zend_string_equal_content(p->key, name)))) {
669-
retval = &p->val;
670-
goto exit;
671-
}
672-
}
673-
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_DYNAMIC_PROPERTY_OFFSET);
674-
}
675-
retval = zend_hash_find(zobj->properties, name);
676-
if (EXPECTED(retval)) {
677-
if (cache_slot) {
678-
uintptr_t idx = (char*)retval - (char*)zobj->properties->arData;
679-
CACHE_PTR_EX(cache_slot + 1, (void*)ZEND_ENCODE_DYN_PROP_OFFSET(idx));
680-
}
681-
goto exit;
682-
}
688+
retval = zend_std_read_dynamic_property(zobj, name, cache_slot, property_offset);
689+
if (retval) {
690+
goto exit;
683691
}
684692
} else if (UNEXPECTED(EG(exception))) {
685693
retval = &EG(uninitialized_zval);
@@ -710,6 +718,13 @@ ZEND_API zval *zend_std_read_property(zend_object *zobj, zend_string *name, int
710718
}
711719

712720
zval_ptr_dtor(&tmp_result);
721+
722+
retval = zend_std_read_dynamic_property(zobj, name, cache_slot, property_offset);
723+
if (retval) {
724+
OBJ_RELEASE(zobj);
725+
goto exit;
726+
}
727+
713728
if (zobj->ce->__get && !((*guard) & IN_GET)) {
714729
goto call_getter;
715730
}

0 commit comments

Comments
 (0)