mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Implement GH-9826: Make class_alias() work with internal classes (#10483)
We can't increase the refcount of internal classes during request time. To work around this problem we simply don't refcount aliases anymore and add a check in the destruction to skip aliases entirely. There were also some checks which checked for an alias implicitly by comparing the refcount, these have been replaced by checking the type of the zval instead.
This commit is contained in:
parent
f4313a81ee
commit
821fc55a68
@ -64,6 +64,7 @@ PHP 8.3 UPGRADE NOTES
|
||||
"full" => bool
|
||||
"buffer_size" => int
|
||||
See GH-9336
|
||||
. class_alias() now supports creating an alias of an internal class.
|
||||
|
||||
- MBString:
|
||||
. mb_strtolower, mb_strtotitle, and mb_convert_case implement conditional
|
||||
|
@ -30,6 +30,10 @@ PHP 8.3 INTERNALS UPGRADE NOTES
|
||||
for C99 features have been removed and therefore macro definitions
|
||||
from php_config.h have disappeared. Do not use those feature
|
||||
macros.
|
||||
* Internal class aliases created during request time can now exist in
|
||||
the class table. zend_register_class_alias_ex() will not increase
|
||||
the refcount for class aliases and the cleanup function takes this
|
||||
into account.
|
||||
|
||||
========================
|
||||
2. Build system changes
|
||||
|
@ -1419,8 +1419,7 @@ static void zend_foreach_op_array_helper(
|
||||
|
||||
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
|
||||
{
|
||||
zend_class_entry *ce;
|
||||
zend_string *key;
|
||||
zval *zv;
|
||||
zend_op_array *op_array;
|
||||
|
||||
zend_foreach_op_array_helper(&script->main_op_array, func, context);
|
||||
@ -1429,10 +1428,11 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
|
||||
zend_foreach_op_array_helper(op_array, func, context);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
|
||||
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
|
||||
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
|
||||
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
|
||||
continue;
|
||||
}
|
||||
zend_class_entry *ce = Z_CE_P(zv);
|
||||
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
|
||||
if (op_array->scope == ce
|
||||
&& op_array->type == ZEND_USER_FUNCTION
|
||||
@ -1468,11 +1468,10 @@ static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx
|
||||
|
||||
ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
|
||||
{
|
||||
zend_class_entry *ce;
|
||||
zend_string *key;
|
||||
zend_op_array *op_array;
|
||||
zend_string *name;
|
||||
zend_optimizer_ctx ctx;
|
||||
zval *zv;
|
||||
|
||||
ctx.arena = zend_arena_create(64 * 1024);
|
||||
ctx.script = script;
|
||||
@ -1599,10 +1598,11 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
|
||||
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
|
||||
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
|
||||
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
|
||||
continue;
|
||||
}
|
||||
zend_class_entry *ce = Z_CE_P(zv);
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
|
||||
if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
|
||||
zend_op_array *orig_op_array =
|
||||
|
@ -3,12 +3,11 @@ Testing creation of alias to an internal class
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
try {
|
||||
class_alias('stdclass', 'foo');
|
||||
} catch (ValueError $exception) {
|
||||
echo $exception->getMessage() . "\n";
|
||||
}
|
||||
class_alias('stdclass', 'foo');
|
||||
$foo = new foo();
|
||||
var_dump($foo);
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
class_alias(): Argument #1 ($class) must be a user-defined class name, internal class name given
|
||||
object(stdClass)#1 (0) {
|
||||
}
|
||||
|
@ -3302,13 +3302,15 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
|
||||
|
||||
lcname = zend_new_interned_string(lcname);
|
||||
|
||||
/* We cannot increase the refcount of an internal class during request time.
|
||||
* Instead of having to deal with differentiating between class types and lifetimes,
|
||||
* we simply don't increase the refcount of a class entry for aliases.
|
||||
*/
|
||||
ZVAL_ALIAS_PTR(&zv, ce);
|
||||
|
||||
ret = zend_hash_add(CG(class_table), lcname, &zv);
|
||||
zend_string_release_ex(lcname, 0);
|
||||
if (ret) {
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
ce->refcount++;
|
||||
}
|
||||
// avoid notifying at MINIT time
|
||||
if (ce->type == ZEND_USER_CLASS) {
|
||||
zend_observer_class_linked_notify(ce, lcname);
|
||||
|
@ -1074,16 +1074,11 @@ ZEND_FUNCTION(class_alias)
|
||||
ce = zend_lookup_class_ex(class_name, NULL, !autoload ? ZEND_FETCH_CLASS_NO_AUTOLOAD : 0);
|
||||
|
||||
if (ce) {
|
||||
if (ce->type == ZEND_USER_CLASS) {
|
||||
if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, 0) == SUCCESS) {
|
||||
RETURN_TRUE;
|
||||
} else {
|
||||
zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(alias_name));
|
||||
RETURN_FALSE;
|
||||
}
|
||||
if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, false) == SUCCESS) {
|
||||
RETURN_TRUE;
|
||||
} else {
|
||||
zend_argument_value_error(1, "must be a user-defined class name, internal class name given");
|
||||
RETURN_THROWS();
|
||||
zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(alias_name));
|
||||
RETURN_FALSE;
|
||||
}
|
||||
} else {
|
||||
zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(class_name));
|
||||
|
@ -301,6 +301,12 @@ ZEND_API void destroy_zend_class(zval *zv)
|
||||
return;
|
||||
}
|
||||
|
||||
/* We don't increase the refcount for class aliases,
|
||||
* skip the destruction of aliases entirely. */
|
||||
if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
|
||||
zend_class_constant *c;
|
||||
zval *p, *end;
|
||||
@ -323,6 +329,8 @@ ZEND_API void destroy_zend_class(zval *zv)
|
||||
return;
|
||||
}
|
||||
|
||||
ZEND_ASSERT(ce->refcount > 0);
|
||||
|
||||
if (--ce->refcount > 0) {
|
||||
return;
|
||||
}
|
||||
@ -516,7 +524,7 @@ void zend_class_add_ref(zval *zv)
|
||||
{
|
||||
zend_class_entry *ce = Z_PTR_P(zv);
|
||||
|
||||
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
if (Z_TYPE_P(zv) != IS_ALIAS_PTR && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
|
||||
ce->refcount++;
|
||||
}
|
||||
}
|
||||
|
@ -700,15 +700,15 @@ ZEND_FUNCTION(opcache_get_status)
|
||||
}
|
||||
|
||||
if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) {
|
||||
zend_class_entry *ce;
|
||||
zval *zv;
|
||||
zend_string *key;
|
||||
|
||||
array_init(&scripts);
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ZCSG(preload_script)->script.class_table, key, ce) {
|
||||
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
|
||||
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(&ZCSG(preload_script)->script.class_table, key, zv) {
|
||||
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
|
||||
add_next_index_str(&scripts, key);
|
||||
} else {
|
||||
add_next_index_str(&scripts, ce->name);
|
||||
add_next_index_str(&scripts, Z_CE_P(zv)->name);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
add_assoc_zval(&statistics, "classes", &scripts);
|
||||
|
Loading…
Reference in New Issue
Block a user