Skip to content

Commit 821fc55

Browse files
authored
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.
1 parent f4313a8 commit 821fc55

8 files changed

+40
-31
lines changed

UPGRADING

+1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ PHP 8.3 UPGRADE NOTES
6464
"full" => bool
6565
"buffer_size" => int
6666
See GH-9336
67+
. class_alias() now supports creating an alias of an internal class.
6768

6869
- MBString:
6970
. mb_strtolower, mb_strtotitle, and mb_convert_case implement conditional

UPGRADING.INTERNALS

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ PHP 8.3 INTERNALS UPGRADE NOTES
3030
for C99 features have been removed and therefore macro definitions
3131
from php_config.h have disappeared. Do not use those feature
3232
macros.
33+
* Internal class aliases created during request time can now exist in
34+
the class table. zend_register_class_alias_ex() will not increase
35+
the refcount for class aliases and the cleanup function takes this
36+
into account.
3337

3438
========================
3539
2. Build system changes

Zend/Optimizer/zend_optimizer.c

+8-8
Original file line numberDiff line numberDiff line change
@@ -1419,8 +1419,7 @@ static void zend_foreach_op_array_helper(
14191419

14201420
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
14211421
{
1422-
zend_class_entry *ce;
1423-
zend_string *key;
1422+
zval *zv;
14241423
zend_op_array *op_array;
14251424

14261425
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
14291428
zend_foreach_op_array_helper(op_array, func, context);
14301429
} ZEND_HASH_FOREACH_END();
14311430

1432-
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1433-
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1431+
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
1432+
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
14341433
continue;
14351434
}
1435+
zend_class_entry *ce = Z_CE_P(zv);
14361436
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
14371437
if (op_array->scope == ce
14381438
&& op_array->type == ZEND_USER_FUNCTION
@@ -1468,11 +1468,10 @@ static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx
14681468

14691469
ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
14701470
{
1471-
zend_class_entry *ce;
1472-
zend_string *key;
14731471
zend_op_array *op_array;
14741472
zend_string *name;
14751473
zend_optimizer_ctx ctx;
1474+
zval *zv;
14761475

14771476
ctx.arena = zend_arena_create(64 * 1024);
14781477
ctx.script = script;
@@ -1599,10 +1598,11 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
15991598
}
16001599
}
16011600

1602-
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1603-
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1601+
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
1602+
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
16041603
continue;
16051604
}
1605+
zend_class_entry *ce = Z_CE_P(zv);
16061606
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
16071607
if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
16081608
zend_op_array *orig_op_array =

Zend/tests/class_alias_006.phpt

+5-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ Testing creation of alias to an internal class
33
--FILE--
44
<?php
55

6-
try {
7-
class_alias('stdclass', 'foo');
8-
} catch (ValueError $exception) {
9-
echo $exception->getMessage() . "\n";
10-
}
6+
class_alias('stdclass', 'foo');
7+
$foo = new foo();
8+
var_dump($foo);
119

1210
?>
1311
--EXPECT--
14-
class_alias(): Argument #1 ($class) must be a user-defined class name, internal class name given
12+
object(stdClass)#1 (0) {
13+
}

Zend/zend_API.c

+5-3
Original file line numberDiff line numberDiff line change
@@ -3302,13 +3302,15 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_
33023302

33033303
lcname = zend_new_interned_string(lcname);
33043304

3305+
/* We cannot increase the refcount of an internal class during request time.
3306+
* Instead of having to deal with differentiating between class types and lifetimes,
3307+
* we simply don't increase the refcount of a class entry for aliases.
3308+
*/
33053309
ZVAL_ALIAS_PTR(&zv, ce);
3310+
33063311
ret = zend_hash_add(CG(class_table), lcname, &zv);
33073312
zend_string_release_ex(lcname, 0);
33083313
if (ret) {
3309-
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
3310-
ce->refcount++;
3311-
}
33123314
// avoid notifying at MINIT time
33133315
if (ce->type == ZEND_USER_CLASS) {
33143316
zend_observer_class_linked_notify(ce, lcname);

Zend/zend_builtin_functions.c

+4-9
Original file line numberDiff line numberDiff line change
@@ -1074,16 +1074,11 @@ ZEND_FUNCTION(class_alias)
10741074
ce = zend_lookup_class_ex(class_name, NULL, !autoload ? ZEND_FETCH_CLASS_NO_AUTOLOAD : 0);
10751075

10761076
if (ce) {
1077-
if (ce->type == ZEND_USER_CLASS) {
1078-
if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, 0) == SUCCESS) {
1079-
RETURN_TRUE;
1080-
} else {
1081-
zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(alias_name));
1082-
RETURN_FALSE;
1083-
}
1077+
if (zend_register_class_alias_ex(ZSTR_VAL(alias_name), ZSTR_LEN(alias_name), ce, false) == SUCCESS) {
1078+
RETURN_TRUE;
10841079
} else {
1085-
zend_argument_value_error(1, "must be a user-defined class name, internal class name given");
1086-
RETURN_THROWS();
1080+
zend_error(E_WARNING, "Cannot declare %s %s, because the name is already in use", zend_get_object_type(ce), ZSTR_VAL(alias_name));
1081+
RETURN_FALSE;
10871082
}
10881083
} else {
10891084
zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(class_name));

Zend/zend_opcode.c

+9-1
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ ZEND_API void destroy_zend_class(zval *zv)
301301
return;
302302
}
303303

304+
/* We don't increase the refcount for class aliases,
305+
* skip the destruction of aliases entirely. */
306+
if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) {
307+
return;
308+
}
309+
304310
if (ce->ce_flags & ZEND_ACC_FILE_CACHED) {
305311
zend_class_constant *c;
306312
zval *p, *end;
@@ -323,6 +329,8 @@ ZEND_API void destroy_zend_class(zval *zv)
323329
return;
324330
}
325331

332+
ZEND_ASSERT(ce->refcount > 0);
333+
326334
if (--ce->refcount > 0) {
327335
return;
328336
}
@@ -516,7 +524,7 @@ void zend_class_add_ref(zval *zv)
516524
{
517525
zend_class_entry *ce = Z_PTR_P(zv);
518526

519-
if (!(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
527+
if (Z_TYPE_P(zv) != IS_ALIAS_PTR && !(ce->ce_flags & ZEND_ACC_IMMUTABLE)) {
520528
ce->refcount++;
521529
}
522530
}

ext/opcache/zend_accelerator_module.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -700,15 +700,15 @@ ZEND_FUNCTION(opcache_get_status)
700700
}
701701

702702
if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) {
703-
zend_class_entry *ce;
703+
zval *zv;
704704
zend_string *key;
705705

706706
array_init(&scripts);
707-
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ZCSG(preload_script)->script.class_table, key, ce) {
708-
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
707+
ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(&ZCSG(preload_script)->script.class_table, key, zv) {
708+
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
709709
add_next_index_str(&scripts, key);
710710
} else {
711-
add_next_index_str(&scripts, ce->name);
711+
add_next_index_str(&scripts, Z_CE_P(zv)->name);
712712
}
713713
} ZEND_HASH_FOREACH_END();
714714
add_assoc_zval(&statistics, "classes", &scripts);

0 commit comments

Comments
 (0)