diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 084c47f45bc47..04244b0de626a 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -8057,8 +8057,11 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) ce->ce_flags |= ZEND_ACC_LINKED; zend_observer_class_linked_notify(ce, lcname); return; + } else { + goto link_unbound; } } else if (!extends_ast) { +link_unbound: /* Link unbound simple class */ zend_build_properties_info_table(ce); ce->ce_flags |= ZEND_ACC_LINKED; @@ -8098,11 +8101,17 @@ static void zend_compile_class_decl(znode *result, zend_ast *ast, bool toplevel) zend_add_literal_string(&key); opline->opcode = ZEND_DECLARE_CLASS; - if (extends_ast && toplevel + if (toplevel && (CG(compiler_options) & ZEND_COMPILE_DELAYED_BINDING) /* We currently don't early-bind classes that implement interfaces or use traits */ && !ce->num_interfaces && !ce->num_traits ) { + if (!extends_ast) { + /* Use empty string for classes without parents to avoid new handler, and special + * handling of zend_early_binding. */ + opline->op2_type = IS_CONST; + LITERAL_STR(opline->op2, ZSTR_EMPTY_ALLOC()); + } CG(active_op_array)->fn_flags |= ZEND_ACC_EARLY_BINDING; opline->opcode = ZEND_DECLARE_CLASS_DELAYED; opline->extended_value = zend_alloc_cache_slot(); diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index ff3a4d7080751..17bbae8335445 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -3276,8 +3276,17 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_ inheritance_status status; zend_class_entry *proto = NULL; zend_class_entry *orig_linking_class; - uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE; + if (ce->ce_flags & ZEND_ACC_LINKED) { + ZEND_ASSERT(ce->parent == NULL); + if (UNEXPECTED(!register_early_bound_ce(delayed_early_binding, lcname, ce))) { + return NULL; + } + zend_observer_class_linked_notify(ce, lcname); + return ce; + } + + uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE; UPDATE_IS_CACHEABLE(parent_ce); if (is_cacheable) { if (zend_inheritance_cache_get && zend_inheritance_cache_add) { diff --git a/ext/opcache/tests/gh8846-1.inc b/ext/opcache/tests/gh8846-1.inc new file mode 100644 index 0000000000000..6169e1cfea4a9 --- /dev/null +++ b/ext/opcache/tests/gh8846-1.inc @@ -0,0 +1,4 @@ + +--CLEAN-- + +--EXPECTF-- +bool(true) +
+Fatal error: Cannot declare class Foo, because the name is already in use in %sgh8846-2.inc on line %d
+ +bool(true) +Ok diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 1a67e2b6e661b..e021270eff3d3 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -357,9 +357,10 @@ static void zend_accel_do_delayed_early_binding( zval *zv = zend_hash_find_known_hash(EG(class_table), early_binding->rtd_key); if (zv) { zend_class_entry *orig_ce = Z_CE_P(zv); - zend_class_entry *parent_ce = - zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1); - if (parent_ce) { + zend_class_entry *parent_ce = !(orig_ce->ce_flags & ZEND_ACC_LINKED) + ? zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1) + : NULL; + if (parent_ce || (orig_ce->ce_flags & ZEND_ACC_LINKED)) { ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv); } }