diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c
index 084c47f45bc47..9d28ff2eddf8f 100644
--- a/Zend/zend_compile.c
+++ b/Zend/zend_compile.c
@@ -8098,7 +8098,7 @@ 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
diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c
index ff3a4d7080751..043cfdbd8f7d9 100644
--- a/Zend/zend_inheritance.c
+++ b/Zend/zend_inheritance.c
@@ -3184,6 +3184,10 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
/* Check whether early binding is prevented due to unresolved types in inheritance checks. */
static inheritance_status zend_can_early_bind(zend_class_entry *ce, zend_class_entry *parent_ce) /* {{{ */
{
+ if (parent_ce == NULL) {
+ return INHERITANCE_SUCCESS;
+ }
+
zend_string *key;
zend_function *parent_func;
zend_property_info *parent_info;
@@ -3278,7 +3282,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
zend_class_entry *orig_linking_class;
uint32_t is_cacheable = ce->ce_flags & ZEND_ACC_IMMUTABLE;
- UPDATE_IS_CACHEABLE(parent_ce);
+ if (parent_ce) {
+ UPDATE_IS_CACHEABLE(parent_ce);
+ }
if (is_cacheable) {
if (zend_inheritance_cache_get && zend_inheritance_cache_add) {
zend_class_entry *ret = zend_inheritance_cache_get(ce, parent_ce, NULL);
@@ -3321,7 +3327,9 @@ ZEND_API zend_class_entry *zend_try_early_bind(zend_class_entry *ce, zend_class_
zend_begin_record_errors();
}
- zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
+ if (parent_ce) {
+ zend_do_inheritance_ex(ce, parent_ce, status == INHERITANCE_SUCCESS);
+ }
if (parent_ce && parent_ce->num_interfaces) {
zend_do_inherit_interfaces(ce, parent_ce);
}
diff --git a/ext/opcache/tests/gh8846.phpt b/ext/opcache/tests/gh8846.phpt
new file mode 100644
index 0000000000000..93caea12d8dd8
--- /dev/null
+++ b/ext/opcache/tests/gh8846.phpt
@@ -0,0 +1,59 @@
+--TEST--
+Bug GH-8846: Always use delayed early binding over early binding in OPcache
+--EXTENSIONS--
+opcache
+--CONFLICTS--
+server
+--INI--
+opcache.validate_timestamps=1
+opcache.revalidate_freq=0
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+bool(true)
+
+Fatal error: Cannot declare class Foo, because the name is already in use in %sgh8846-2.php on line %d
+
+bool(true)
+Ok
diff --git a/ext/opcache/tests/php_cli_server.inc b/ext/opcache/tests/php_cli_server.inc
index 8d15b81c790b9..20b2b8f053256 100644
--- a/ext/opcache/tests/php_cli_server.inc
+++ b/ext/opcache/tests/php_cli_server.inc
@@ -11,7 +11,7 @@ function php_cli_server_start($ini = "") {
$ini_array = array_map(function($arg) {
return trim($arg, '\'"');
}, $ini_array);
- $cmd = [$php_executable, '-t', $doc_root, '-n', ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
+ $cmd = [$php_executable, '-t', $doc_root, ...$ini_array, '-S', PHP_CLI_SERVER_ADDRESS];
$descriptorspec = array(
0 => STDIN,
1 => STDOUT,
diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c
index b99a50b212828..b7c9923b1f33d 100644
--- a/ext/opcache/zend_accelerator_util_funcs.c
+++ b/ext/opcache/zend_accelerator_util_funcs.c
@@ -282,8 +282,9 @@ void zend_accel_build_delayed_early_binding_list(zend_persistent_script *persist
zval *lcname = RT_CONSTANT(opline, opline->op1);
early_binding->lcname = zend_string_copy(Z_STR_P(lcname));
early_binding->rtd_key = zend_string_copy(Z_STR_P(lcname + 1));
- early_binding->lc_parent_name =
- zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)));
+ early_binding->lc_parent_name = opline->op2_type != IS_UNUSED
+ ? zend_string_copy(Z_STR_P(RT_CONSTANT(opline, opline->op2)))
+ : NULL;
early_binding->cache_slot = (uint32_t) -1;
early_binding++;
}
@@ -328,7 +329,9 @@ void zend_accel_free_delayed_early_binding_list(zend_persistent_script *persiste
zend_early_binding *early_binding = &persistent_script->early_bindings[i];
zend_string_release(early_binding->lcname);
zend_string_release(early_binding->rtd_key);
- zend_string_release(early_binding->lc_parent_name);
+ if (early_binding->lc_parent_name) {
+ zend_string_release(early_binding->lc_parent_name);
+ }
}
efree(persistent_script->early_bindings);
persistent_script->early_bindings = NULL;
@@ -357,10 +360,13 @@ 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) {
- ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
+ if (early_binding->lc_parent_name) {
+ zend_class_entry *parent_ce = zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1);
+ if (parent_ce) {
+ ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv);
+ }
+ } else {
+ ce = zend_try_early_bind(orig_ce, NULL, early_binding->lcname, zv);
}
}
}
diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c
index e21aaa069348a..7db7c987d8a0a 100644
--- a/ext/opcache/zend_persist.c
+++ b/ext/opcache/zend_persist.c
@@ -1306,7 +1306,9 @@ static zend_early_binding *zend_persist_early_bindings(
for (uint32_t i = 0; i < num_early_bindings; i++) {
zend_accel_store_interned_string(early_bindings[i].lcname);
zend_accel_store_interned_string(early_bindings[i].rtd_key);
- zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
+ if (early_bindings[i].lc_parent_name) {
+ zend_accel_store_interned_string(early_bindings[i].lc_parent_name);
+ }
}
}
return early_bindings;
diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c
index dfc281eb7f6f7..bb544b7d28f36 100644
--- a/ext/opcache/zend_persist_calc.c
+++ b/ext/opcache/zend_persist_calc.c
@@ -584,7 +584,9 @@ static void zend_persist_early_bindings_calc(
zend_early_binding *early_binding = &early_bindings[i];
ADD_INTERNED_STRING(early_binding->lcname);
ADD_INTERNED_STRING(early_binding->rtd_key);
- ADD_INTERNED_STRING(early_binding->lc_parent_name);
+ if (early_binding->lc_parent_name) {
+ ADD_INTERNED_STRING(early_binding->lc_parent_name);
+ }
}
}