diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0dbdaa2b35e..7257efa3799 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -3136,13 +3136,11 @@ static zend_always_inline bool register_early_bound_ce(zval *delayed_early_bindi if (EXPECTED(!(ce->ce_flags & ZEND_ACC_PRELOADED))) { if (zend_hash_set_bucket_key(EG(class_table), (Bucket *)delayed_early_binding, lcname) != NULL) { Z_CE_P(delayed_early_binding) = ce; - zend_observer_class_linked_notify(ce, lcname); return true; } } else { /* If preloading is used, don't replace the existing bucket, add a new one. */ if (zend_hash_add_ptr(EG(class_table), lcname, ce) != NULL) { - zend_observer_class_linked_notify(ce, lcname); return true; } } @@ -3150,7 +3148,6 @@ static zend_always_inline bool register_early_bound_ce(zval *delayed_early_bindi return false; } if (zend_hash_add_ptr(CG(class_table), lcname, ce) != NULL) { - zend_observer_class_linked_notify(ce, lcname); return true; } return false; @@ -3171,6 +3168,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ret))) { return NULL; } + zend_observer_class_linked_notify(ret, lcname); return ret; } } else { @@ -3245,6 +3243,7 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ if (ZSTR_HAS_CE_CACHE(ce->name)) { ZSTR_SET_CE_CACHE(ce->name, ce); } + zend_observer_class_linked_notify(ce, lcname); return ce; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 91765676b38..dcff7a78832 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -723,7 +723,11 @@ static void zend_persist_class_method(zval *zv, zend_class_entry *ce) } } } - ZEND_MAP_PTR_NEW(op_array->run_time_cache); + // Real dynamically created internal functions like enum methods must have their own run_time_cache pointer. They're always on the same scope as their defining class. + // However, copies - as caused by inheritance of internal methods - must retain the original run_time_cache pointer, shared with the source function. + if (!op_array->scope || op_array->scope == ce) { + ZEND_MAP_PTR_NEW(op_array->run_time_cache); + } } } return; diff --git a/ext/zend_test/tests/gh9871.phpt b/ext/zend_test/tests/gh9871.phpt new file mode 100644 index 00000000000..c2b6f359bd6 --- /dev/null +++ b/ext/zend_test/tests/gh9871.phpt @@ -0,0 +1,33 @@ +--TEST-- +Test observing inherited internal functions +--EXTENSIONS-- +zend_test +--INI-- +opcache.enable_cli=1 +zend_test.observer.enabled=1 +zend_test.observer.observe_all=1 +zend_test.observer.show_return_value=1 +--FILE-- +setAccessible(true); + +?> +--EXPECT-- + + + + + + + + +