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); + } } }