Skip to content

Implement GH-9826: Make class_alias() work with internal classes #10483

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions UPGRADING
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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
Expand Down
4 changes: 4 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
16 changes: 8 additions & 8 deletions Zend/Optimizer/zend_optimizer.c
Original file line number Diff line number Diff line change
Expand Up @@ -1416,8 +1416,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);
Expand All @@ -1426,10 +1425,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
Expand Down Expand Up @@ -1465,11 +1465,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;
Expand Down Expand Up @@ -1596,10 +1595,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 =
Expand Down
11 changes: 5 additions & 6 deletions Zend/tests/class_alias_006.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
8 changes: 5 additions & 3 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 4 additions & 9 deletions Zend/zend_builtin_functions.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
10 changes: 9 additions & 1 deletion Zend/zend_opcode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -323,6 +329,8 @@ ZEND_API void destroy_zend_class(zval *zv)
return;
}

ZEND_ASSERT(ce->refcount > 0);

if (--ce->refcount > 0) {
return;
}
Expand Down Expand Up @@ -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++;
}
}
Expand Down
8 changes: 4 additions & 4 deletions ext/opcache/zend_accelerator_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down