Skip to content

Commit 799d05b

Browse files
committed
Implement GH-9826: Make class_alias() work with internal classes
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 d5ce616 commit 799d05b

8 files changed

+38
-34
lines changed

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ PHP 8.3 UPGRADE NOTES
5757
"full" => bool
5858
"buffer_size" => int
5959
See GH-9336
60+
. class_alias() now supports creating an alias of an internal class.
6061

6162
- MBString:
6263
. mb_strtolower, mb_strtotitle, and mb_convert_case implement conditional

UPGRADING.INTERNALS

Lines changed: 4 additions & 0 deletions
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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1416,8 +1416,7 @@ static void zend_foreach_op_array_helper(
14161416

14171417
void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void *context)
14181418
{
1419-
zend_class_entry *ce;
1420-
zend_string *key;
1419+
zval *zv;
14211420
zend_op_array *op_array;
14221421

14231422
zend_foreach_op_array_helper(&script->main_op_array, func, context);
@@ -1426,10 +1425,11 @@ void zend_foreach_op_array(zend_script *script, zend_op_array_func_t func, void
14261425
zend_foreach_op_array_helper(op_array, func, context);
14271426
} ZEND_HASH_FOREACH_END();
14281427

1429-
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1430-
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1428+
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
1429+
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
14311430
continue;
14321431
}
1432+
zend_class_entry *ce = Z_PTR_P(zv);
14331433
ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) {
14341434
if (op_array->scope == ce
14351435
&& op_array->type == ZEND_USER_FUNCTION
@@ -1465,11 +1465,10 @@ static void zend_optimizer_call_registered_passes(zend_script *script, void *ctx
14651465

14661466
ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_level, zend_long debug_level)
14671467
{
1468-
zend_class_entry *ce;
1469-
zend_string *key;
14701468
zend_op_array *op_array;
14711469
zend_string *name;
14721470
zend_optimizer_ctx ctx;
1471+
zval *zv;
14731472

14741473
ctx.arena = zend_arena_create(64 * 1024);
14751474
ctx.script = script;
@@ -1596,10 +1595,11 @@ ZEND_API void zend_optimize_script(zend_script *script, zend_long optimization_l
15961595
}
15971596
}
15981597

1599-
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&script->class_table, key, ce) {
1600-
if (ce->refcount > 1 && !zend_string_equals_ci(key, ce->name)) {
1598+
ZEND_HASH_MAP_FOREACH_VAL(&script->class_table, zv) {
1599+
if (Z_TYPE_P(zv) == IS_ALIAS_PTR) {
16011600
continue;
16021601
}
1602+
zend_class_entry *ce = Z_PTR_P(zv);
16031603
ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(&ce->function_table, name, op_array) {
16041604
if (op_array->scope != ce && op_array->type == ZEND_USER_FUNCTION) {
16051605
zend_op_array *orig_op_array =

Zend/tests/class_alias_006.phpt

Lines changed: 5 additions & 6 deletions
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

Lines changed: 5 additions & 3 deletions
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

Lines changed: 4 additions & 9 deletions
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

Lines changed: 9 additions & 1 deletion
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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -700,16 +700,11 @@ ZEND_FUNCTION(opcache_get_status)
700700
}
701701

702702
if (zend_hash_num_elements(&ZCSG(preload_script)->script.class_table)) {
703-
zend_class_entry *ce;
704703
zend_string *key;
705704

706705
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)) {
709-
add_next_index_str(&scripts, key);
710-
} else {
711-
add_next_index_str(&scripts, ce->name);
712-
}
706+
ZEND_HASH_MAP_FOREACH_STR_KEY(&ZCSG(preload_script)->script.class_table, key) {
707+
add_next_index_str(&scripts, key);
713708
} ZEND_HASH_FOREACH_END();
714709
add_assoc_zval(&statistics, "classes", &scripts);
715710
}

0 commit comments

Comments
 (0)