From 52cab0a476ba4fd915e3fbfe482e636682eba1ce Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Jun 2025 16:15:50 -0700 Subject: [PATCH 01/30] Initial zend_class_alias Probably going to have a bunch of failures but I can't find any more locally --- Zend/zend_API.c | 15 ++-- Zend/zend_builtin_functions.c | 3 +- Zend/zend_class_alias.c | 29 ++++++++ Zend/zend_class_alias.h | 53 ++++++++++++++ Zend/zend_execute_API.c | 6 +- Zend/zend_extensions.c | 8 ++- Zend/zend_observer.c | 5 +- Zend/zend_types.h | 7 +- configure.ac | 1 + ext/opcache/ZendAccelerator.c | 88 ++++++++++++++++------- ext/opcache/zend_accelerator_util_funcs.c | 30 ++++++-- ext/opcache/zend_persist_calc.c | 16 ++++- ext/reflection/php_reflection.c | 9 ++- 13 files changed, 223 insertions(+), 47 deletions(-) create mode 100644 Zend/zend_class_alias.c create mode 100644 Zend/zend_class_alias.h diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 211a5d14e6c3a..5ed6d2cb85626 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -35,6 +35,7 @@ #include "zend_enum.h" #include "zend_object_handlers.h" #include "zend_observer.h" +#include "zend_class_alias.h" #include @@ -2537,7 +2538,9 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ } ZEND_HASH_FOREACH_END(); /* Collect internal classes with static members */ - ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_VAL(CG(class_table), ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); if (ce->type == ZEND_INTERNAL_CLASS && ce->default_static_members_count > 0) { class_count++; @@ -2551,7 +2554,8 @@ ZEND_API void zend_collect_module_handlers(void) /* {{{ */ class_cleanup_handlers[class_count] = NULL; if (class_count) { - ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { + ZEND_HASH_MAP_FOREACH_VAL(CG(class_table), ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); if (ce->type == ZEND_INTERNAL_CLASS && ce->default_static_members_count > 0) { class_cleanup_handlers[--class_count] = ce; @@ -3296,8 +3300,9 @@ static void clean_module_classes(int module_number) /* {{{ */ { /* Child classes may reuse structures from parent classes, so destroy in reverse order. */ Bucket *bucket; + zend_class_entry *ce; ZEND_HASH_REVERSE_FOREACH_BUCKET(EG(class_table), bucket) { - const zend_class_entry *ce = Z_CE(bucket->val); + Z_CE_FROM_ZVAL(ce, bucket->val); if (ce->type == ZEND_INTERNAL_CLASS && ce->info.internal.module->module_number == module_number) { zend_hash_del_bucket(EG(class_table), bucket); } @@ -3610,7 +3615,9 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ * 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); + zend_class_alias *alias = zend_class_alias_init(ce); + + ZVAL_ALIAS_PTR(&zv, alias); ret = zend_hash_add(CG(class_table), lcname, &zv); zend_string_release_ex(lcname, 0); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index c16d8e74f2bde..f5e1b057c9795 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -18,6 +18,7 @@ */ #include "zend.h" +#include "zend_class_alias.h" #include "zend_API.h" #include "zend_attributes.h" #include "zend_gc.h" @@ -1433,7 +1434,7 @@ static inline void get_declared_class_impl(INTERNAL_FUNCTION_PARAMETERS, int fla zend_hash_real_init_packed(Z_ARRVAL_P(return_value)); ZEND_HASH_FILL_PACKED(Z_ARRVAL_P(return_value)) { ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, zv) { - ce = Z_PTR_P(zv); + Z_CE_FROM_ZVAL_P(ce, zv); if ((ce->ce_flags & (ZEND_ACC_LINKED|ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT)) == flags && key && ZSTR_VAL(key)[0] != 0) { diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c new file mode 100644 index 0000000000000..a8d4e0c6988cd --- /dev/null +++ b/Zend/zend_class_alias.c @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Scherzer | + +----------------------------------------------------------------------+ +*/ + +#include "zend_class_alias.h" + +zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { + zend_class_alias *alias = malloc(sizeof(zend_class_alias)); + // refcount field is only there for compatibility with other structures + GC_SET_REFCOUNT(alias, 1); + + alias->ce = ce; + + return alias; +} diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h new file mode 100644 index 0000000000000..fdf0c61a6514c --- /dev/null +++ b/Zend/zend_class_alias.h @@ -0,0 +1,53 @@ +/* + +----------------------------------------------------------------------+ + | Zend Engine | + +----------------------------------------------------------------------+ + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.00 of the Zend license, | + | that is bundled with this package in the file LICENSE, and is | + | available through the world-wide-web at the following url: | + | http://www.zend.com/license/2_00.txt. | + | If you did not receive a copy of the Zend license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@zend.com so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Authors: Daniel Scherzer | + +----------------------------------------------------------------------+ +*/ + +#ifndef ZEND_CLASS_ALIAS_H +#define ZEND_CLASS_ALIAS_H + +#include "zend_types.h" + +struct _zend_class_alias { + zend_refcounted_h gc; + zend_class_entry *ce; +}; + +typedef struct _zend_class_alias zend_class_alias; + +#define Z_CE_FROM_ZVAL_P(_ce, _zv) do { \ + if (EXPECTED(Z_TYPE_P(_zv) == IS_PTR)) { \ + _ce = Z_PTR_P(_zv); \ + } else { \ + ZEND_ASSERT(Z_TYPE_P(_zv) == IS_ALIAS_PTR); \ + _ce = Z_CLASS_ALIAS_P(_zv)->ce; \ + } \ + } while (0) \ + + +#define Z_CE_FROM_ZVAL(_ce, _zv) do { \ + if (EXPECTED(Z_TYPE(_zv) == IS_PTR)) { \ + _ce = Z_PTR(_zv); \ + } else { \ + ZEND_ASSERT(Z_TYPE(_zv) == IS_ALIAS_PTR); \ + _ce = Z_CLASS_ALIAS(_zv)->ce; \ + } \ + } while (0) \ + + +zend_class_alias * zend_class_alias_init(zend_class_entry *ce); + +#endif diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index dbd2a9039cfc9..e9ed58cc200ff 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -22,6 +22,7 @@ #include #include "zend.h" +#include "zend_class_alias.h" #include "zend_compile.h" #include "zend_execute.h" #include "zend_API.h" @@ -326,7 +327,8 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { - zend_class_entry *ce = Z_PTR_P(zv); + zend_class_entry *ce; + Z_CE_FROM_ZVAL_P(ce, zv); if (ce->default_static_members_count) { zend_cleanup_internal_class_data(ce); @@ -1198,7 +1200,7 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * if (!key) { zend_string_release_ex(lc_name, 0); } - ce = (zend_class_entry*)Z_PTR_P(zv); + Z_CE_FROM_ZVAL_P(ce, zv); if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && diff --git a/Zend/zend_extensions.c b/Zend/zend_extensions.c index a4e5a38f90d89..72c59626850fa 100644 --- a/Zend/zend_extensions.c +++ b/Zend/zend_extensions.c @@ -17,6 +17,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_class_alias.h" #include "zend_extensions.h" #include "zend_system_id.h" @@ -327,7 +328,9 @@ ZEND_API void zend_init_internal_run_time_cache(void) { if (rt_size) { size_t functions = zend_hash_num_elements(CG(function_table)); zend_class_entry *ce; - ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_VAL(CG(class_table), ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); functions += zend_hash_num_elements(&ce->function_table); } ZEND_HASH_FOREACH_END(); @@ -344,7 +347,8 @@ ZEND_API void zend_init_internal_run_time_cache(void) { ptr += rt_size; } } ZEND_HASH_FOREACH_END(); - ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { + ZEND_HASH_MAP_FOREACH_VAL(CG(class_table), ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) { if (!ZEND_USER_CODE(zif->type) && ZEND_MAP_PTR_GET(zif->run_time_cache) == NULL) { ZEND_MAP_PTR_SET(zif->run_time_cache, (void *)ptr); diff --git a/Zend/zend_observer.c b/Zend/zend_observer.c index bee20bdbc20dc..c2c5cc11b7df3 100644 --- a/Zend/zend_observer.c +++ b/Zend/zend_observer.c @@ -20,6 +20,7 @@ #include "zend_observer.h" +#include "zend_class_alias.h" #include "zend_extensions.h" #include "zend_llist.h" #include "zend_vm.h" @@ -89,7 +90,9 @@ ZEND_API void zend_observer_post_startup(void) ++zif->T; } ZEND_HASH_FOREACH_END(); zend_class_entry *ce; - ZEND_HASH_MAP_FOREACH_PTR(CG(class_table), ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_VAL(CG(class_table), ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, zif) { ++zif->T; } ZEND_HASH_FOREACH_END(); diff --git a/Zend/zend_types.h b/Zend/zend_types.h index 22dbfa9be879b..4eb247ad99dac 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -88,6 +88,7 @@ typedef struct _zend_resource zend_resource; typedef struct _zend_reference zend_reference; typedef struct _zend_ast_ref zend_ast_ref; typedef struct _zend_ast zend_ast; +typedef struct _zend_class_alias zend_class_alias; typedef int (*compare_func_t)(const void *, const void *); typedef void (*swap_func_t)(void *, void *); @@ -335,6 +336,7 @@ typedef union _zend_value { void *ptr; zend_class_entry *ce; zend_function *func; + zend_class_alias *class_alias; struct { uint32_t w1; uint32_t w2; @@ -1069,6 +1071,9 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { #define Z_PTR(zval) (zval).value.ptr #define Z_PTR_P(zval_p) Z_PTR(*(zval_p)) +#define Z_CLASS_ALIAS(zval) (zval).value.class_alias +#define Z_CLASS_ALIAS_P(zval_p) Z_CLASS_ALIAS(*(zval_p)) + #define ZVAL_UNDEF(z) do { \ Z_TYPE_INFO_P(z) = IS_UNDEF; \ } while (0) @@ -1281,7 +1286,7 @@ static zend_always_inline uint32_t zval_gc_info(uint32_t gc_type_info) { } while (0) #define ZVAL_ALIAS_PTR(z, p) do { \ - Z_PTR_P(z) = (p); \ + Z_CLASS_ALIAS_P(z) = (p); \ Z_TYPE_INFO_P(z) = IS_ALIAS_PTR; \ } while (0) diff --git a/configure.ac b/configure.ac index 214c0ab91b2a5..522c66ed284ca 100644 --- a/configure.ac +++ b/configure.ac @@ -1735,6 +1735,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_autoload.c zend_builtin_functions.c zend_call_stack.c + zend_class_alias.c zend_closures.c zend_compile.c zend_constants.c diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index f4134212bc4eb..8ea018eabc2d6 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -22,6 +22,7 @@ #include "main/php.h" #include "main/php_globals.h" #include "zend.h" +#include "zend_class_alias.h" #include "zend_extensions.h" #include "zend_compile.h" #include "ZendAccelerator.h" @@ -687,7 +688,7 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) { zend_class_entry *ce; - ce = (zend_class_entry*)Z_PTR(p->val); + Z_CE_FROM_ZVAL(ce, p->val); if (p->key) { p->key = new_interned_string(p->key); @@ -3657,8 +3658,9 @@ static void preload_shutdown(void) } if (EG(class_table)) { + zend_class_entry *ce; ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { - zend_class_entry *ce = Z_PTR_P(zv); + Z_CE_FROM_ZVAL_P(ce, zv); if (ce->type == ZEND_INTERNAL_CLASS && Z_TYPE_P(zv) != IS_ALIAS_PTR) { break; } @@ -3746,7 +3748,8 @@ static void preload_move_user_classes(HashTable *src, HashTable *dst) src->pDestructor = NULL; zend_hash_extend(dst, dst->nNumUsed + src->nNumUsed, 0); ZEND_HASH_MAP_FOREACH_BUCKET_FROM(src, p, EG(persistent_classes_count)) { - zend_class_entry *ce = Z_PTR(p->val); + zend_class_entry *ce; + Z_CE_FROM_ZVAL(ce, p->val); /* Possible with internal class aliases */ if (ce->type == ZEND_INTERNAL_CLASS) { @@ -3810,17 +3813,27 @@ static void preload_sort_classes(void *base, size_t count, size_t siz, compare_f while (b1 < end) { try_again: - ce = (zend_class_entry*)Z_PTR(b1->val); + Z_CE_FROM_ZVAL(ce, b1->val); if (ce->parent && (ce->ce_flags & ZEND_ACC_LINKED)) { p = ce->parent; if (p->type == ZEND_USER_CLASS) { b2 = b1 + 1; while (b2 < end) { - if (p == Z_PTR(b2->val)) { - tmp = *b1; - *b1 = *b2; - *b2 = tmp; - goto try_again; + if (Z_TYPE(b2->val) == IS_ALIAS_PTR) { + if (p == Z_CLASS_ALIAS(b2->val)->ce) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } + } else { + ZEND_ASSERT(Z_TYPE(b2->val) == IS_PTR); + if (p == Z_PTR(b2->val)) { + tmp = *b1; + *b1 = *b2; + *b2 = tmp; + goto try_again; + } } b2++; } @@ -3859,9 +3872,9 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e if (ce->parent_name) { zend_string *key = zend_string_tolower(ce->parent_name); - zend_class_entry *parent = zend_hash_find_ptr(EG(class_table), key); + zval *parent_entry = zend_hash_find(EG(class_table), key); zend_string_release(key); - if (!parent) { + if (!parent_entry) { error->kind = "Unknown parent "; error->name = ZSTR_VAL(ce->parent_name); return FAILURE; @@ -3870,9 +3883,9 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e if (ce->num_interfaces) { for (uint32_t i = 0; i < ce->num_interfaces; i++) { - zend_class_entry *interface = - zend_hash_find_ptr(EG(class_table), ce->interface_names[i].lc_name); - if (!interface) { + zval *interface_entry = + zend_hash_find(EG(class_table), ce->interface_names[i].lc_name); + if (!interface_entry) { error->kind = "Unknown interface "; error->name = ZSTR_VAL(ce->interface_names[i].name); return FAILURE; @@ -3882,9 +3895,9 @@ static zend_result preload_resolve_deps(preload_error *error, const zend_class_e if (ce->num_traits) { for (uint32_t i = 0; i < ce->num_traits; i++) { - zend_class_entry *trait = - zend_hash_find_ptr(EG(class_table), ce->trait_names[i].lc_name); - if (!trait) { + zval *trait_entry = + zend_hash_find(EG(class_table), ce->trait_names[i].lc_name); + if (!trait_entry) { error->kind = "Unknown trait "; error->name = ZSTR_VAL(ce->trait_names[i].name); return FAILURE; @@ -4057,7 +4070,7 @@ static void preload_link(void) changed = false; ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), key, zv, EG(persistent_classes_count)) { - ce = Z_PTR_P(zv); + Z_CE_FROM_ZVAL_P(ce, zv); /* Possible with internal class aliases */ if (ce->type == ZEND_INTERNAL_CLASS) { @@ -4120,13 +4133,26 @@ static void preload_link(void) } zend_catch { /* Clear variance obligations that were left behind on bailout. */ if (CG(delayed_variance_obligations)) { + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + zend_hash_index_del( + CG(delayed_variance_obligations), (uintptr_t) Z_CLASS_ALIAS_P(zv)->ce); + } else { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); + zend_hash_index_del( + CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv)); + } zend_hash_index_del( CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv)); } /* Restore the original class. */ zv = zend_hash_set_bucket_key(EG(class_table), (Bucket*)zv, key); - Z_CE_P(zv) = orig_ce; + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + Z_CLASS_ALIAS_P(zv)->ce = orig_ce; + } else { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); + Z_CE_P(zv) = orig_ce; + } orig_ce->ce_flags &= ~temporary_flags; zend_arena_release(&CG(arena), checkpoint); @@ -4148,8 +4174,7 @@ static void preload_link(void) changed = false; ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { - ce = Z_PTR_P(zv); - + Z_CE_FROM_ZVAL_P(ce, zv); /* Possible with internal class aliases */ if (ce->type == ZEND_INTERNAL_CLASS) { if (Z_TYPE_P(zv) != IS_ALIAS_PTR) { @@ -4173,7 +4198,7 @@ static void preload_link(void) /* Warn for classes that could not be linked. */ ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM( EG(class_table), key, zv, EG(persistent_classes_count)) { - ce = Z_PTR_P(zv); + Z_CE_FROM_ZVAL_P(ce, zv); /* Possible with internal class aliases */ if (ce->type == ZEND_INTERNAL_CLASS) { @@ -4227,7 +4252,11 @@ static void preload_link(void) ZEND_ASSERT(op_array->type == ZEND_USER_FUNCTION); preload_remove_declares(op_array); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_MAP_FOREACH_PTR_FROM(EG(class_table), ce, EG(persistent_classes_count)) { + zend_string *_unused; + (void)_unused; + // No ZEND_HASH_MAP_FOREACH_VAL_FROM + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL_FROM(EG(class_table), _unused, zv, EG(persistent_classes_count)) { + Z_CE_FROM_ZVAL_P(ce, zv); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { if (op_array->type == ZEND_USER_FUNCTION) { preload_remove_declares(op_array); @@ -4427,18 +4456,21 @@ static void preload_fix_trait_methods(zend_class_entry *ce) static void preload_optimize(zend_persistent_script *script) { zend_class_entry *ce; + zval *zv; zend_persistent_script *tmp_script; zend_shared_alloc_init_xlat_table(); - ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_VAL(&script->script.class_table, zv) { + Z_CE_FROM_ZVAL_P(ce, zv); if (ce->ce_flags & ZEND_ACC_TRAIT) { preload_register_trait_methods(ce); } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, tmp_script) { - ZEND_HASH_MAP_FOREACH_PTR(&tmp_script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_VAL(&tmp_script->script.class_table, zv) { + Z_CE_FROM_ZVAL_P(ce, zv); if (ce->ce_flags & ZEND_ACC_TRAIT) { preload_register_trait_methods(ce); } @@ -4448,12 +4480,14 @@ static void preload_optimize(zend_persistent_script *script) zend_optimize_script(&script->script, ZCG(accel_directives).optimization_level, ZCG(accel_directives).opt_debug_level); zend_accel_finalize_delayed_early_binding_list(script); - ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_VAL(&script->script.class_table, zv) { + Z_CE_FROM_ZVAL_P(ce, zv); preload_fix_trait_methods(ce); } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_PTR(preload_scripts, script) { - ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + ZEND_HASH_MAP_FOREACH_VAL(&script->script.class_table, zv) { + Z_CE_FROM_ZVAL_P(ce, zv); preload_fix_trait_methods(ce); } ZEND_HASH_FOREACH_END(); } ZEND_HASH_FOREACH_END(); diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 2de7854fb14c5..f99a80ff4e55c 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -20,6 +20,7 @@ */ #include "zend_API.h" +#include "zend_class_alias.h" #include "zend_constants.h" #include "zend_inheritance.h" #include "zend_accelerator_util_funcs.h" @@ -122,10 +123,14 @@ void zend_accel_move_user_classes(HashTable *src, uint32_t count, zend_script *s p = end - count; for (; p != end; p++) { if (UNEXPECTED(Z_TYPE(p->val) == IS_UNDEF)) continue; - ce = Z_PTR(p->val); + Z_CE_FROM_ZVAL(ce, p->val); if (EXPECTED(ce->type == ZEND_USER_CLASS) && EXPECTED(ce->info.user.filename == filename)) { - _zend_hash_append_ptr(dst, p->key, ce); + if (Z_TYPE(p->val) == IS_ALIAS_PTR) { + _zend_hash_append(dst, p->key, &p->val); + } else { + _zend_hash_append_ptr(dst, p->key, ce); + } zend_hash_del_bucket(src, p); } } @@ -209,19 +214,32 @@ static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, co * value. */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { - const zend_class_entry *ce1 = Z_PTR(p->val); + zend_class_entry *ce1; + Z_CE_FROM_ZVAL(ce1, p->val); if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) { CG(in_compilation) = 1; zend_set_compiled_filename(ce1->info.user.filename); CG(zend_lineno) = ce1->info.user.line_start; - zend_class_redeclaration_error(E_ERROR, Z_PTR_P(t)); + if (Z_TYPE_P(t) == IS_ALIAS_PTR) { + zend_class_redeclaration_error(E_ERROR, Z_CLASS_ALIAS(p->val)->ce); + } else { + ZEND_ASSERT(Z_TYPE_P(t) == IS_PTR); + zend_class_redeclaration_error(E_ERROR, Z_PTR_P(t)); + } return; } continue; } } else { - zend_class_entry *ce = Z_PTR(p->val); - _zend_hash_append_ptr_ex(target, p->key, Z_PTR(p->val), 1); + zend_class_entry *ce; + if (Z_TYPE(p->val) == IS_ALIAS_PTR) { + _zend_hash_append_ex(target, p->key, &p->val, 1); + ce = Z_CLASS_ALIAS(p->val)->ce; + } else { + ZEND_ASSERT(Z_TYPE(p->val) == IS_PTR); + ce = Z_PTR(p->val); + _zend_hash_append_ptr_ex(target, p->key, ce, 1); + } if ((ce->ce_flags & ZEND_ACC_LINKED) && ZSTR_VAL(p->key)[0]) { if (ZSTR_HAS_CE_CACHE(ce->name)) { ZSTR_SET_CE_CACHE_EX(ce->name, ce, 0); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 0b0ff51d0d4df..98f7ad3324e12 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -27,6 +27,7 @@ #include "zend_operators.h" #include "zend_attributes.h" #include "zend_constants.h" +#include "zend_class_alias.h" #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) @@ -596,6 +597,14 @@ void zend_persist_class_entry_calc(zend_class_entry *ce) } } +static void zend_persist_class_alias_entry_calc(const zend_class_alias *alias) +{ + // alias->ce is going to be a pointer to a class entry that will be + // persisted on its own, here we just need to add size for the alias + ADD_SIZE(sizeof(zend_class_alias)); + zend_persist_class_entry_calc(alias->ce); +} + static void zend_accel_persist_class_table_calc(const HashTable *class_table) { Bucket *p; @@ -604,7 +613,12 @@ static void zend_accel_persist_class_table_calc(const HashTable *class_table) ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); ADD_INTERNED_STRING(p->key); - zend_persist_class_entry_calc(Z_CE(p->val)); + if (Z_TYPE(p->val) == IS_PTR) { + zend_persist_class_entry_calc(Z_CE(p->val)); + } else { + ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR); + zend_persist_class_alias_entry_calc(Z_CLASS_ALIAS(p->val)); + } } ZEND_HASH_FOREACH_END(); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ee49d2d90f507..9dafa26660837 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -18,6 +18,7 @@ +----------------------------------------------------------------------+ */ +#include "zend_class_alias.h" #include "zend_compile.h" #include "zend_execute.h" #include "zend_lazy_objects.h" @@ -7049,7 +7050,9 @@ ZEND_METHOD(ReflectionExtension, getClasses) GET_REFLECTION_OBJECT_PTR(module); array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); add_extension_class(ce, key, return_value, module, true); } ZEND_HASH_FOREACH_END(); } @@ -7067,7 +7070,9 @@ ZEND_METHOD(ReflectionExtension, getClassNames) GET_REFLECTION_OBJECT_PTR(module); array_init(return_value); - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); add_extension_class(ce, key, return_value, module, false); } ZEND_HASH_FOREACH_END(); } From 32afb73f34aa1adca3fa7213fe0e003765dfa07d Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Jun 2025 16:28:51 -0700 Subject: [PATCH 02/30] Add to windows build --- win32/build/config.w32 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/build/config.w32 b/win32/build/config.w32 index aefcfb5f82474..0f233d86bae5e 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c zend_autoload.c"); + zend_lazy_objects.c zend_autoload.c zend_class_alias.h"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({ From 1adf20b7cf67355ebf8ff5a47448ebb96aae538a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Jun 2025 17:08:35 -0700 Subject: [PATCH 03/30] Add #[ClassAlias] --- .../class_alias/parameter_invalid.phpt | 11 ++++ .../class_alias/parameter_nonstring.phpt | 11 ++++ .../class_alias/parameter_required.phpt | 15 +++++ .../class_alias/redeclaration_error.phpt | 11 ++++ .../class_alias/repeated_attribute.phpt | 25 +++++++ .../class_alias/target_validation.phpt | 11 ++++ .../class_alias/working_example.phpt | 17 +++++ Zend/zend_attributes.c | 66 +++++++++++++++++++ Zend/zend_attributes.stub.php | 11 ++++ Zend/zend_attributes_arginfo.h | 33 +++++++++- 10 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/attributes/class_alias/parameter_invalid.phpt create mode 100644 Zend/tests/attributes/class_alias/parameter_nonstring.phpt create mode 100644 Zend/tests/attributes/class_alias/parameter_required.phpt create mode 100644 Zend/tests/attributes/class_alias/redeclaration_error.phpt create mode 100644 Zend/tests/attributes/class_alias/repeated_attribute.phpt create mode 100644 Zend/tests/attributes/class_alias/target_validation.phpt create mode 100644 Zend/tests/attributes/class_alias/working_example.phpt diff --git a/Zend/tests/attributes/class_alias/parameter_invalid.phpt b/Zend/tests/attributes/class_alias/parameter_invalid.phpt new file mode 100644 index 0000000000000..028c31414b41e --- /dev/null +++ b/Zend/tests/attributes/class_alias/parameter_invalid.phpt @@ -0,0 +1,11 @@ +--TEST-- +Alias name must be valid +--FILE-- + +--EXPECTF-- +Fatal error: Cannot use "never" as a class alias as it is reserved in %s on line %d diff --git a/Zend/tests/attributes/class_alias/parameter_nonstring.phpt b/Zend/tests/attributes/class_alias/parameter_nonstring.phpt new file mode 100644 index 0000000000000..6be25a38e61b9 --- /dev/null +++ b/Zend/tests/attributes/class_alias/parameter_nonstring.phpt @@ -0,0 +1,11 @@ +--TEST-- +Parameter must be a string +--FILE-- + +--EXPECTF-- +Fatal error: ClassAlias::__construct(): Argument #1 ($alias) must be of type string, array given in %s on line %d diff --git a/Zend/tests/attributes/class_alias/parameter_required.phpt b/Zend/tests/attributes/class_alias/parameter_required.phpt new file mode 100644 index 0000000000000..24ea4fe895685 --- /dev/null +++ b/Zend/tests/attributes/class_alias/parameter_required.phpt @@ -0,0 +1,15 @@ +--TEST-- +Parameter is required +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ArgumentCountError: ClassAlias::__construct() expects exactly 1 argument, 0 given in %s:%d +Stack trace: +#0 %s(%d): ClassAlias->__construct() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/redeclaration_error.phpt b/Zend/tests/attributes/class_alias/redeclaration_error.phpt new file mode 100644 index 0000000000000..642d08760a645 --- /dev/null +++ b/Zend/tests/attributes/class_alias/redeclaration_error.phpt @@ -0,0 +1,11 @@ +--TEST-- +Cannot redeclare an existing class +--FILE-- + +--EXPECTF-- +Fatal error: Unable to declare alias 'Attribute' for 'Demo' in %s on line %d diff --git a/Zend/tests/attributes/class_alias/repeated_attribute.phpt b/Zend/tests/attributes/class_alias/repeated_attribute.phpt new file mode 100644 index 0000000000000..f2b6fa1617868 --- /dev/null +++ b/Zend/tests/attributes/class_alias/repeated_attribute.phpt @@ -0,0 +1,25 @@ +--TEST-- +Attribute can be repeated +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +object(Demo)#%d (0) { +} +object(Demo)#%d (0) { +} diff --git a/Zend/tests/attributes/class_alias/target_validation.phpt b/Zend/tests/attributes/class_alias/target_validation.phpt new file mode 100644 index 0000000000000..4dbf6fadc6ce1 --- /dev/null +++ b/Zend/tests/attributes/class_alias/target_validation.phpt @@ -0,0 +1,11 @@ +--TEST-- +Can only be used on classes +--FILE-- + +--EXPECTF-- +Fatal error: Attribute "ClassAlias" cannot target function (allowed targets: class) in %s on line %d diff --git a/Zend/tests/attributes/class_alias/working_example.phpt b/Zend/tests/attributes/class_alias/working_example.phpt new file mode 100644 index 0000000000000..ea1ae4476f395 --- /dev/null +++ b/Zend/tests/attributes/class_alias/working_example.phpt @@ -0,0 +1,17 @@ +--TEST-- +Working usage +--FILE-- + +--EXPECTF-- +bool(true) +object(Demo)#%d (0) { +} diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index cba95810ba496..5f78b23fe532e 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -33,6 +33,7 @@ ZEND_API zend_class_entry *zend_ce_override; ZEND_API zend_class_entry *zend_ce_deprecated; ZEND_API zend_class_entry *zend_ce_nodiscard; ZEND_API zend_class_entry *zend_ce_delayed_target_validation; +ZEND_API zend_class_entry *zend_ce_class_alias; static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; @@ -129,6 +130,46 @@ static zend_string *validate_deprecated( } +static void validate_class_alias( + zend_attribute *attr, uint32_t target, zend_class_entry *scope) +{ + zval alias_obj; + ZVAL_UNDEF(&alias_obj); + zend_result result = zend_get_attribute_object( + &alias_obj, + zend_ce_class_alias, + attr, + scope, + scope->info.user.filename + ); + if (result == FAILURE) { + ZEND_ASSERT(EG(exception)); + return; + } + + zval *alias_name = zend_read_property( + zend_ce_class_alias, + Z_OBJ(alias_obj), + ZEND_STRL("alias"), + false, + NULL + ); + result = zend_register_class_alias_ex( + Z_STRVAL_P(alias_name), + Z_STRLEN_P(alias_name), + scope, + false + ); + if (result == FAILURE) { + zend_error_noreturn(E_ERROR, "Unable to declare alias '%s' for '%s'", + Z_STRVAL_P(alias_name), + ZSTR_VAL(scope->name) + ); + } + zval_ptr_dtor(alias_name); + zval_ptr_dtor(&alias_obj); +} + ZEND_METHOD(Attribute, __construct) { zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL; @@ -265,6 +306,24 @@ ZEND_METHOD(NoDiscard, __construct) } } +ZEND_METHOD(ClassAlias, __construct) +{ + zend_string *alias = NULL; + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(alias) + ZEND_PARSE_PARAMETERS_END(); + + zval value; + ZVAL_STR(&value, alias); + zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("alias"), &value); + + /* The assignment might fail due to 'readonly'. */ + if (UNEXPECTED(EG(exception))) { + RETURN_THROWS(); + } +} + static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { if (attributes) { @@ -602,10 +661,17 @@ void zend_register_attribute_ce(void) zend_ce_nodiscard = register_class_NoDiscard(); attr = zend_mark_internal_attribute(zend_ce_nodiscard); +<<<<<<< HEAD attr->validator = validate_nodiscard; zend_ce_delayed_target_validation = register_class_DelayedTargetValidation(); attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); +======= + + zend_ce_class_alias = register_class_ClassAlias(); + attr = zend_mark_internal_attribute(zend_ce_class_alias); + attr->validator = validate_class_alias; +>>>>>>> e1a37a8b7b7 (Add #[ClassAlias]) } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index ded9c89593a36..e928c91167d0e 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -103,3 +103,14 @@ public function __construct(?string $message = null) {} */ #[Attribute(Attribute::TARGET_ALL)] final class DelayedTargetValidation {} + +/** + * @strict-properties + */ +#[Attribute(Attribute::TARGET_CLASS|Attribute::IS_REPEATABLE)] +final class ClassAlias +{ + public readonly string $alias; + + public function __construct(string $alias) {} +} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 54a66af29966d..8279c4c256ffc 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */ + * Stub hash: 16234f8eb8aaf201a5a337849311632fcd8a5e7d */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -33,6 +33,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ClassAlias___construct, 0, 0, 1) + ZEND_ARG_TYPE_INFO(0, alias, IS_STRING, 0) +ZEND_END_ARG_INFO() + ZEND_METHOD(Attribute, __construct); ZEND_METHOD(ReturnTypeWillChange, __construct); ZEND_METHOD(AllowDynamicProperties, __construct); @@ -43,6 +47,7 @@ ZEND_METHOD(SensitiveParameterValue, __debugInfo); ZEND_METHOD(Override, __construct); ZEND_METHOD(Deprecated, __construct); ZEND_METHOD(NoDiscard, __construct); +ZEND_METHOD(ClassAlias, __construct); static const zend_function_entry class_Attribute_methods[] = { ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC) @@ -86,6 +91,11 @@ static const zend_function_entry class_NoDiscard_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ClassAlias_methods[] = { + ZEND_ME(ClassAlias, __construct, arginfo_class_ClassAlias___construct, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -291,3 +301,24 @@ static zend_class_entry *register_class_DelayedTargetValidation(void) return class_entry; } + +static zend_class_entry *register_class_ClassAlias(void) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ClassAlias", class_ClassAlias_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); + + zval property_alias_default_value; + ZVAL_UNDEF(&property_alias_default_value); + zend_string *property_alias_name = zend_string_init("alias", sizeof("alias") - 1, true); + zend_declare_typed_property(class_entry, property_alias_name, &property_alias_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release_ex(property_alias_name, true); + + zend_string *attribute_name_Attribute_class_ClassAlias_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); + zend_attribute *attribute_Attribute_class_ClassAlias_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ClassAlias_0, 1); + zend_string_release_ex(attribute_name_Attribute_class_ClassAlias_0, true); + ZVAL_LONG(&attribute_Attribute_class_ClassAlias_0->args[0].value, ZEND_ATTRIBUTE_TARGET_CLASS | ZEND_ATTRIBUTE_IS_REPEATABLE); + + return class_entry; +} From cb19a98b41bbcd14dc4ceb58d107526d3d191557 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Jun 2025 17:14:48 -0700 Subject: [PATCH 04/30] Reflection fixes --- ext/reflection/php_reflection.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9dafa26660837..b0b4ea8d9fe60 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1225,8 +1225,10 @@ static void _extension_string(smart_str *str, const zend_module_entry *module, c zend_string *key; zend_class_entry *ce; int num_classes = 0; + zval *ce_or_alias; - ZEND_HASH_MAP_FOREACH_STR_KEY_PTR(EG(class_table), key, ce) { + ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EG(class_table), key, ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); _extension_class_string(ce, key, &str_classes, ZSTR_VAL(sub_indent), module, &num_classes); } ZEND_HASH_FOREACH_END(); if (num_classes) { @@ -5435,9 +5437,11 @@ ZEND_METHOD(ReflectionClass, getTraitAliases) zend_string *lcname = zend_string_tolower(cur_ref->method_name); for (j = 0; j < ce->num_traits; j++) { - zend_class_entry *trait = - zend_hash_find_ptr(CG(class_table), ce->trait_names[j].lc_name); - ZEND_ASSERT(trait && "Trait must exist"); + zval *trait_entry = + zend_hash_find(CG(class_table), ce->trait_names[j].lc_name); + ZEND_ASSERT(trait_entry && "Trait must exist"); + zend_class_entry *trait; + Z_CE_FROM_ZVAL_P(trait, trait_entry); if (zend_hash_exists(&trait->function_table, lcname)) { class_name = trait->name; break; From aa6036727979a1cc8de4cf653057ee84b848eecd Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Fri, 6 Jun 2025 18:10:50 -0700 Subject: [PATCH 05/30] Save reflection work Need to actually add zend_class_alias->attributes but ran out of time for now --- ...class_alias_listed_as_target-internal.phpt | 11 +++ ...class_alias_listed_as_target-userland.phpt | 31 +++++++ .../must_target_class_alias-internal.phpt | 11 +++ .../must_target_class_alias-userland.phpt | 25 ++++++ .../not_repeatable-internal.phpt | 12 +++ .../not_repeatable-userland.phpt | 24 ++++++ .../repeatable-internal.phpt | 15 ++++ .../repeatable-userland.phpt | 28 +++++++ ...arget_all_targets_class_alias-default.phpt | 25 ++++++ ...rget_all_targets_class_alias-explicit.phpt | 25 ++++++ Zend/zend_attributes.c | 19 ++++- Zend/zend_attributes.h | 7 +- Zend/zend_attributes.stub.php | 6 +- Zend/zend_attributes_arginfo.h | 11 ++- ext/reflection/php_reflection.c | 83 +++++++++++++++++++ ext/reflection/php_reflection.stub.php | 15 ++++ ext/reflection/php_reflection_arginfo.h | 33 +++++++- ext/reflection/php_reflection_decl.h | 8 +- 18 files changed, 375 insertions(+), 14 deletions(-) create mode 100644 Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt create mode 100644 Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt create mode 100644 Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt create mode 100644 Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt create mode 100644 Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt create mode 100644 Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt create mode 100644 Zend/tests/attributes/class_alias_target/repeatable-internal.phpt create mode 100644 Zend/tests/attributes/class_alias_target/repeatable-userland.phpt create mode 100644 Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt create mode 100644 Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt diff --git a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt new file mode 100644 index 0000000000000..4f3bbb6fcb0af --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt @@ -0,0 +1,11 @@ +--TEST-- +Class alias listed in valid targets when used wrong (internal attribute) +--FILE-- + +--EXPECTF-- +Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant, class alias) in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt new file mode 100644 index 0000000000000..cccedd532a2ab --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt @@ -0,0 +1,31 @@ +--TEST-- +Class alias listed in valid targets when used wrong (userland attribute) +--FILE-- +getAttributes(); +var_dump($attribs); +$attribs[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(16) "MyAliasAttribute" + } +} + +Fatal error: Uncaught Error: Attribute "MyAliasAttribute" cannot target class (allowed targets: class alias) in %s:%d +Stack trace: +#0 %s(%d): ReflectionAttribute->newInstance() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt new file mode 100644 index 0000000000000..3bbc7c1185c24 --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt @@ -0,0 +1,11 @@ +--TEST-- +Error when attribute does not target class alias (internal attribute) +--FILE-- + +--EXPECT-- +NO \ No newline at end of file diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt new file mode 100644 index 0000000000000..6b994757a226e --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt @@ -0,0 +1,25 @@ +--TEST-- +Error when attribute does not target class alias (useland attribute) +--FILE-- +getAttributes(); +var_dump($attribs); +$attribs[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" + } +} diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt new file mode 100644 index 0000000000000..ea20f481db716 --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt @@ -0,0 +1,12 @@ +--TEST-- +Validation of attribute repetition (not allowed; internal attribute) +--FILE-- + +--EXPECT-- + +e \ No newline at end of file diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt new file mode 100644 index 0000000000000..516a5243e55a4 --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt @@ -0,0 +1,24 @@ +--TEST-- +Validation of attribute repetition (not allowed; userland attribute) +--FILE-- +getAttributes(); +var_dump($attributes); +$attributes[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" + } +} diff --git a/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt b/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt new file mode 100644 index 0000000000000..fe8b1b805952a --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt @@ -0,0 +1,15 @@ +--TEST-- +Validation of attribute repetition (is allowed; internal attribute) +--EXTENSIONS-- +zend_test +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt new file mode 100644 index 0000000000000..aa2f6511e63f0 --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt @@ -0,0 +1,28 @@ +--TEST-- +Validation of attribute repetition (is allowed; userland attribute) +--FILE-- +getAttributes(); +var_dump($attributes); +$attributes[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" + } +} +php: /usr/src/php/Zend/zend_variables.c:143: zval_copy_ctor_func: Assertion `!(zval_gc_flags(((*(zvalue)).value.str)->gc.u.type_info) & (1<<6))' failed. +Aborted (core dumped) + +Termsig=6 diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt new file mode 100644 index 0000000000000..22abb9504264c --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt @@ -0,0 +1,25 @@ +--TEST-- +Attributes with TARGET_ALL (from the default) can target class aliases +--FILE-- +getAttributes(); +var_dump($attribs); +$attribs[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" + } +} diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt new file mode 100644 index 0000000000000..15772aafe7bc0 --- /dev/null +++ b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt @@ -0,0 +1,25 @@ +--TEST-- +Attributes with TARGET_ALL (from an explicit parameter) can target class aliases +--FILE-- +getAttributes(); +var_dump($attribs); +$attribs[0]->newInstance(); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" + } +} diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 5f78b23fe532e..990103099d428 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -309,9 +309,12 @@ ZEND_METHOD(NoDiscard, __construct) ZEND_METHOD(ClassAlias, __construct) { zend_string *alias = NULL; + HashTable *attributes = NULL; - ZEND_PARSE_PARAMETERS_START(1, 1) + ZEND_PARSE_PARAMETERS_START(1, 2) Z_PARAM_STR(alias) + Z_PARAM_OPTIONAL + Z_PARAM_ARRAY_HT(attributes) ZEND_PARSE_PARAMETERS_END(); zval value; @@ -322,6 +325,17 @@ ZEND_METHOD(ClassAlias, __construct) if (UNEXPECTED(EG(exception))) { RETURN_THROWS(); } + + if (attributes == NULL || zend_hash_num_elements(attributes) == 0) { + return; + } + + if (!zend_array_is_list(attributes)) { + zend_throw_error(NULL, + "ClassAlias::__construct(): Argument #2 ($attributes) must be a list, not an associative array" + ); + RETURN_THROWS(); + } } static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) @@ -486,7 +500,8 @@ static const char *target_names[] = { "property", "class constant", "parameter", - "constant" + "constant", + "class alias" }; ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index f8b61ac9d1666..218e4cb9d85e3 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -30,9 +30,10 @@ #define ZEND_ATTRIBUTE_TARGET_CLASS_CONST (1<<4) #define ZEND_ATTRIBUTE_TARGET_PARAMETER (1<<5) #define ZEND_ATTRIBUTE_TARGET_CONST (1<<6) -#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<7) - 1) -#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<7) -#define ZEND_ATTRIBUTE_FLAGS ((1<<8) - 1) +#define ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS (1<<7) +#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<8) - 1) +#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<8) +#define ZEND_ATTRIBUTE_FLAGS ((1<<9) - 1) /* Flags for zend_attribute.flags */ #define ZEND_ATTRIBUTE_PERSISTENT (1<<0) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index e928c91167d0e..6dc09de2fba26 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -19,6 +19,8 @@ final class Attribute const int TARGET_PARAMETER = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_TARGET_CONST */ const int TARGET_CONSTANT = UNKNOWN; + /** @cvalue ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS */ + const int TARGET_CLASS_ALIAS = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_TARGET_ALL */ const int TARGET_ALL = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_IS_REPEATABLE */ @@ -77,7 +79,7 @@ public function __construct() {} /** * @strict-properties */ -#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT|Attribute::TARGET_CLASS)] +#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT|Attribute::TARGET_CLASS|Attribute::TARGET_CLASS_ALIAS)] final class Deprecated { public readonly ?string $message; @@ -112,5 +114,5 @@ final class ClassAlias { public readonly string $alias; - public function __construct(string $alias) {} + public function __construct(string $alias, array $attributes = []) {} } diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 8279c4c256ffc..a4a46e2ad4895 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: 16234f8eb8aaf201a5a337849311632fcd8a5e7d */ + * Stub hash: b70c1fed22c60805e01f5ec7f34ad8133ac70b28 */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -35,6 +35,7 @@ ZEND_END_ARG_INFO() ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ClassAlias___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, alias, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, attributes, IS_ARRAY, 0, "[]") ZEND_END_ARG_INFO() ZEND_METHOD(Attribute, __construct); @@ -145,6 +146,12 @@ static zend_class_entry *register_class_Attribute(void) zend_declare_typed_class_constant(class_entry, const_TARGET_CONSTANT_name, &const_TARGET_CONSTANT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const_TARGET_CONSTANT_name, true); + zval const_TARGET_CLASS_ALIAS_value; + ZVAL_LONG(&const_TARGET_CLASS_ALIAS_value, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); + zend_string *const_TARGET_CLASS_ALIAS_name = zend_string_init_interned("TARGET_CLASS_ALIAS", sizeof("TARGET_CLASS_ALIAS") - 1, true); + zend_declare_typed_class_constant(class_entry, const_TARGET_CLASS_ALIAS_name, &const_TARGET_CLASS_ALIAS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); + zend_string_release_ex(const_TARGET_CLASS_ALIAS_name, true); + zval const_TARGET_ALL_value; ZVAL_LONG(&const_TARGET_ALL_value, ZEND_ATTRIBUTE_TARGET_ALL); zend_string *const_TARGET_ALL_name = zend_string_init_interned("TARGET_ALL", sizeof("TARGET_ALL") - 1, true); @@ -263,7 +270,7 @@ static zend_class_entry *register_class_Deprecated(void) zend_string *attribute_name_Attribute_class_Deprecated_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); zend_attribute *attribute_Attribute_class_Deprecated_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Deprecated_0, 1); zend_string_release_ex(attribute_name_Attribute_class_Deprecated_0, true); - ZVAL_LONG(&attribute_Attribute_class_Deprecated_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST | ZEND_ATTRIBUTE_TARGET_CLASS); + ZVAL_LONG(&attribute_Attribute_class_Deprecated_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST | ZEND_ATTRIBUTE_TARGET_CLASS | ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); return class_entry; } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index b0b4ea8d9fe60..fa82cc01ab7fd 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -106,6 +106,7 @@ PHPAPI zend_class_entry *reflection_enum_backed_case_ptr; PHPAPI zend_class_entry *reflection_fiber_ptr; PHPAPI zend_class_entry *reflection_constant_ptr; PHPAPI zend_class_entry *reflection_property_hook_type_ptr; +PHPAPI zend_class_entry *reflection_class_alias_ptr; #define GET_REFLECTION_OBJECT() do { \ intern = Z_REFLECTION_P(ZEND_THIS); \ @@ -8151,6 +8152,84 @@ ZEND_METHOD(ReflectionConstant, __toString) RETURN_STR(smart_str_extract(&str)); } +ZEND_METHOD(ReflectionClassAlias, __construct) +{ + zend_string *name; + + zval *object = ZEND_THIS; + reflection_object *intern = Z_REFLECTION_P(object); + + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_STR(name) + ZEND_PARSE_PARAMETERS_END(); + + // First use zend_lookup_class() which will also take care of autoloading, + // but that will always return the underlying class entry + zend_class_entry *ce = zend_lookup_class(name); + if (ce == NULL) { + if (!EG(exception)) { + zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" does not exist", ZSTR_VAL(name)); + } + RETURN_THROWS(); + } + + // We now know that the alias exists, find it somewhere in the class_table + zend_string *lc_name; + if (ZSTR_VAL(name)[0] == '\\') { + lc_name = zend_string_alloc(ZSTR_LEN(name) - 1, 0); + zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(name) + 1, ZSTR_LEN(name) - 1); + } else { + lc_name = zend_string_tolower(name); + } + + zval *entry = zend_hash_find(EG(class_table), lc_name); + ZEND_ASSERT(entry != NULL); + + if (Z_TYPE_P(entry) != IS_ALIAS_PTR) { + ZEND_ASSERT(Z_TYPE_P(entry) == IS_PTR); + zend_throw_exception_ex(reflection_exception_ptr, -1, "\"%s\" is not an alias", ZSTR_VAL(name)); + RETURN_THROWS(); + } + + zend_class_alias *alias = Z_CLASS_ALIAS_P(entry); + + zend_string_release_ex(lc_name, /* persistent */ false); + + intern->ptr = alias; + intern->ref_type = REF_TYPE_OTHER; + + zval *name_zv = reflection_prop_name(object); + zval_ptr_dtor(name_zv); + ZVAL_STR_COPY(name_zv, name); +} + +ZEND_METHOD(ReflectionClassAlias, getAttributes) +{ + reflection_object *intern; + zend_class_alias *alias; + + GET_REFLECTION_OBJECT_PTR(alias); + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, + NULL, 0, alias->ce, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS, + NULL); +} + +ZEND_METHOD(ReflectionClassAlias, __toString) +{ + reflection_object *intern; + zend_class_alias *alias; + smart_str str = {0}; + + ZEND_PARSE_PARAMETERS_NONE(); + + GET_REFLECTION_OBJECT_PTR(alias); + + smart_str_appends(&str, "TODO ReflectionClassAlias::__toString()"); + // _const_string(&str, ZSTR_VAL(const_->name), &const_->value, ""); + RETURN_STR(smart_str_extract(&str)); +} + PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); @@ -8256,6 +8335,10 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ reflection_property_hook_type_ptr = register_class_PropertyHookType(); + reflection_class_alias_ptr = register_class_ReflectionClassAlias(reflector_ptr); + reflection_class_alias_ptr->create_object = reflection_objects_new; + reflection_class_alias_ptr->default_object_handlers = &reflection_object_handlers; + REFLECTION_G(key_initialized) = false; return SUCCESS; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index b0273a3174f8e..bf88a2791daa4 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -939,3 +939,18 @@ public function __toString(): string {} public function getAttributes(?string $name = null, int $flags = 0): array {} } + +/** + * @strict-properties + * @not-serializable + */ +final class ReflectionClassAlias implements Reflector +{ + public string $name; + + public function __construct(string $name) {} + + public function __toString(): string {} + + public function getAttributes(?string $name = null, int $flags = 0): array {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 66605a22bbd66..1f39d0cc2d17f 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 267472e2b726ca5e788eb5cc3e946bc9aa7c9c41 + * Stub hash: 6e955854a092f91f1331acabcd564537a58ff62f * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) @@ -733,6 +733,12 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes +#define arginfo_class_ReflectionClassAlias___construct arginfo_class_ReflectionExtension___construct + +#define arginfo_class_ReflectionClassAlias___toString arginfo_class_ReflectionFunction___toString + +#define arginfo_class_ReflectionClassAlias_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); ZEND_METHOD(ReflectionFunctionAbstract, inNamespace); @@ -1006,6 +1012,9 @@ ZEND_METHOD(ReflectionConstant, getExtension); ZEND_METHOD(ReflectionConstant, getExtensionName); ZEND_METHOD(ReflectionConstant, __toString); ZEND_METHOD(ReflectionConstant, getAttributes); +ZEND_METHOD(ReflectionClassAlias, __construct); +ZEND_METHOD(ReflectionClassAlias, __toString); +ZEND_METHOD(ReflectionClassAlias, getAttributes); static const zend_function_entry class_Reflection_methods[] = { ZEND_ME(Reflection, getModifierNames, arginfo_class_Reflection_getModifierNames, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -1382,6 +1391,13 @@ static const zend_function_entry class_ReflectionConstant_methods[] = { ZEND_FE_END }; +static const zend_function_entry class_ReflectionClassAlias_methods[] = { + ZEND_ME(ReflectionClassAlias, __construct, arginfo_class_ReflectionClassAlias___construct, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassAlias, __toString, arginfo_class_ReflectionClassAlias___toString, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassAlias, getAttributes, arginfo_class_ReflectionClassAlias_getAttributes, ZEND_ACC_PUBLIC) + ZEND_FE_END +}; + static zend_class_entry *register_class_ReflectionException(zend_class_entry *class_entry_Exception) { zend_class_entry ce, *class_entry; @@ -1919,3 +1935,18 @@ static zend_class_entry *register_class_ReflectionConstant(zend_class_entry *cla return class_entry; } + +static zend_class_entry *register_class_ReflectionClassAlias(zend_class_entry *class_entry_Reflector) +{ + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "ReflectionClassAlias", class_ReflectionClassAlias_methods); + class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE); + zend_class_implements(class_entry, 1, class_entry_Reflector); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_declare_typed_property(class_entry, ZSTR_KNOWN(ZEND_STR_NAME), &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + + return class_entry; +} diff --git a/ext/reflection/php_reflection_decl.h b/ext/reflection/php_reflection_decl.h index a5e8affd0beb1..d86ee8d9c6f52 100644 --- a/ext/reflection/php_reflection_decl.h +++ b/ext/reflection/php_reflection_decl.h @@ -1,12 +1,12 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 267472e2b726ca5e788eb5cc3e946bc9aa7c9c41 */ + * Stub hash: 6e955854a092f91f1331acabcd564537a58ff62f */ -#ifndef ZEND_PHP_REFLECTION_DECL_267472e2b726ca5e788eb5cc3e946bc9aa7c9c41_H -#define ZEND_PHP_REFLECTION_DECL_267472e2b726ca5e788eb5cc3e946bc9aa7c9c41_H +#ifndef ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H +#define ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H typedef enum zend_enum_PropertyHookType { ZEND_ENUM_PropertyHookType_Get = 1, ZEND_ENUM_PropertyHookType_Set = 2, } zend_enum_PropertyHookType; -#endif /* ZEND_PHP_REFLECTION_DECL_267472e2b726ca5e788eb5cc3e946bc9aa7c9c41_H */ +#endif /* ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H */ From b3e707d01b3a7f8c49c6f134aef28ffcaf141509 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:10:31 -0700 Subject: [PATCH 06/30] Basic working version! --- Zend/tests/attributes/034_target_values.phpt | 24 +- .../class_alias/attributes_dict.phpt | 19 + .../class_alias/attributes_nonarray.phpt | 14 + .../class_alias/attributes_valid.phpt | 12 + ...rameter_invalid.phpt => name_invalid.phpt} | 0 ...ter_nonstring.phpt => name_nonstring.phpt} | 0 ...meter_required.phpt => name_required.phpt} | 2 +- .../must_target_class_alias-internal.phpt | 4 +- .../must_target_class_alias-userland.phpt | 8 +- .../not_repeatable-internal.phpt | 5 +- .../not_repeatable-userland.phpt | 15 +- .../repeatable-userland.phpt | 13 +- ...arget_all_targets_class_alias-default.phpt | 2 +- ...rget_all_targets_class_alias-explicit.phpt | 2 +- .../constant_listed_as_target-internal.phpt | 2 +- Zend/zend_attributes.c | 350 +++++++++++++++--- Zend/zend_class_alias.c | 3 +- Zend/zend_class_alias.h | 1 + Zend/zend_compile.c | 2 +- Zend/zend_opcode.c | 21 +- ext/reflection/php_reflection.c | 13 +- ...eflectionClassAlias_construct_missing.phpt | 13 + ...lectionClassAlias_construct_non_alias.phpt | 13 + .../ReflectionClassAlias_construct_valid.phpt | 15 + ...lectionClassAlias_getAttributes_empty.phpt | 14 + ...ionClassAlias_getAttributes_non-empty.phpt | 20 + .../tests/ReflectionClassAlias_toString.phpt | 12 + 27 files changed, 498 insertions(+), 101 deletions(-) create mode 100644 Zend/tests/attributes/class_alias/attributes_dict.phpt create mode 100644 Zend/tests/attributes/class_alias/attributes_nonarray.phpt create mode 100644 Zend/tests/attributes/class_alias/attributes_valid.phpt rename Zend/tests/attributes/class_alias/{parameter_invalid.phpt => name_invalid.phpt} (100%) rename Zend/tests/attributes/class_alias/{parameter_nonstring.phpt => name_nonstring.phpt} (100%) rename Zend/tests/attributes/class_alias/{parameter_required.phpt => name_required.phpt} (82%) create mode 100644 ext/reflection/tests/ReflectionClassAlias_construct_missing.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_construct_non_alias.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_construct_valid.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_toString.phpt diff --git a/Zend/tests/attributes/034_target_values.phpt b/Zend/tests/attributes/034_target_values.phpt index e56c0c285fbd6..2a06d39ebf05e 100644 --- a/Zend/tests/attributes/034_target_values.phpt +++ b/Zend/tests/attributes/034_target_values.phpt @@ -16,24 +16,26 @@ showFlag("TARGET_PROPERTY", Attribute::TARGET_PROPERTY); showFlag("TARGET_CLASS_CONSTANT", Attribute::TARGET_CLASS_CONSTANT); showFlag("TARGET_PARAMETER", Attribute::TARGET_PARAMETER); showFlag("TARGET_CONSTANT", Attribute::TARGET_CONSTANT); +showFlag("TARGET_CLASS_ALIAS", Attribute::TARGET_CLASS_ALIAS); showFlag("IS_REPEATABLE", Attribute::IS_REPEATABLE); $all = Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_PARAMETER - | Attribute::TARGET_CONSTANT; + | Attribute::TARGET_CONSTANT | Attribute::TARGET_CLASS_ALIAS; var_dump($all, Attribute::TARGET_ALL, $all === Attribute::TARGET_ALL); ?> --EXPECT-- -Attribute::TARGET_CLASS = 1 (127 & 1 === 1) -Attribute::TARGET_FUNCTION = 2 (127 & 2 === 2) -Attribute::TARGET_METHOD = 4 (127 & 4 === 4) -Attribute::TARGET_PROPERTY = 8 (127 & 8 === 8) -Attribute::TARGET_CLASS_CONSTANT = 16 (127 & 16 === 16) -Attribute::TARGET_PARAMETER = 32 (127 & 32 === 32) -Attribute::TARGET_CONSTANT = 64 (127 & 64 === 64) -Attribute::IS_REPEATABLE = 128 (127 & 128 === 0) -int(127) -int(127) +Attribute::TARGET_CLASS = 1 (255 & 1 === 1) +Attribute::TARGET_FUNCTION = 2 (255 & 2 === 2) +Attribute::TARGET_METHOD = 4 (255 & 4 === 4) +Attribute::TARGET_PROPERTY = 8 (255 & 8 === 8) +Attribute::TARGET_CLASS_CONSTANT = 16 (255 & 16 === 16) +Attribute::TARGET_PARAMETER = 32 (255 & 32 === 32) +Attribute::TARGET_CONSTANT = 64 (255 & 64 === 64) +Attribute::TARGET_CLASS_ALIAS = 128 (255 & 128 === 128) +Attribute::IS_REPEATABLE = 256 (255 & 256 === 0) +int(255) +int(255) bool(true) diff --git a/Zend/tests/attributes/class_alias/attributes_dict.phpt b/Zend/tests/attributes/class_alias/attributes_dict.phpt new file mode 100644 index 0000000000000..0d09e3d0bd29c --- /dev/null +++ b/Zend/tests/attributes/class_alias/attributes_dict.phpt @@ -0,0 +1,19 @@ +--TEST-- +Alias attributes must not be associative +--FILE-- + new Deprecated()])] +class Demo {} + +$attr = new ReflectionClass( Demo::class )->getAttributes()[0]; +$attr->newInstance(); + +?> +--EXPECTF-- +Fatal error: Uncaught Error: ClassAlias::__construct(): Argument #2 ($attributes) must be a list, not an associative array in %s:%d +Stack trace: +#0 %s(%d): ClassAlias->__construct('Other', Array) +#1 %s(%d): ReflectionAttribute->newInstance() +#2 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_nonarray.phpt b/Zend/tests/attributes/class_alias/attributes_nonarray.phpt new file mode 100644 index 0000000000000..a0513c9fc7a8c --- /dev/null +++ b/Zend/tests/attributes/class_alias/attributes_nonarray.phpt @@ -0,0 +1,14 @@ +--TEST-- +Alias attributes must be an array +--FILE-- +getAttributes()[0]; +$attr->newInstance(); + +?> +--EXPECTF-- +Fatal error: ClassAlias::__construct(): Argument #2 ($attributes) must be of type array, true given in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_valid.phpt b/Zend/tests/attributes/class_alias/attributes_valid.phpt new file mode 100644 index 0000000000000..8b790c57ae062 --- /dev/null +++ b/Zend/tests/attributes/class_alias/attributes_valid.phpt @@ -0,0 +1,12 @@ +--TEST-- +Alias attributes do not need to exist for declaration +--FILE-- + +--EXPECT-- +Done diff --git a/Zend/tests/attributes/class_alias/parameter_invalid.phpt b/Zend/tests/attributes/class_alias/name_invalid.phpt similarity index 100% rename from Zend/tests/attributes/class_alias/parameter_invalid.phpt rename to Zend/tests/attributes/class_alias/name_invalid.phpt diff --git a/Zend/tests/attributes/class_alias/parameter_nonstring.phpt b/Zend/tests/attributes/class_alias/name_nonstring.phpt similarity index 100% rename from Zend/tests/attributes/class_alias/parameter_nonstring.phpt rename to Zend/tests/attributes/class_alias/name_nonstring.phpt diff --git a/Zend/tests/attributes/class_alias/parameter_required.phpt b/Zend/tests/attributes/class_alias/name_required.phpt similarity index 82% rename from Zend/tests/attributes/class_alias/parameter_required.phpt rename to Zend/tests/attributes/class_alias/name_required.phpt index 24ea4fe895685..2f907925adc4f 100644 --- a/Zend/tests/attributes/class_alias/parameter_required.phpt +++ b/Zend/tests/attributes/class_alias/name_required.phpt @@ -8,7 +8,7 @@ class Demo {} ?> --EXPECTF-- -Fatal error: Uncaught ArgumentCountError: ClassAlias::__construct() expects exactly 1 argument, 0 given in %s:%d +Fatal error: Uncaught ArgumentCountError: ClassAlias::__construct() expects at least 1 argument, 0 given in %s:%d Stack trace: #0 %s(%d): ClassAlias->__construct() #1 {main} diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt index 3bbc7c1185c24..2e741bea2208a 100644 --- a/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt +++ b/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt @@ -7,5 +7,5 @@ Error when attribute does not target class alias (internal attribute) class Demo {} ?> ---EXPECT-- -NO \ No newline at end of file +--EXPECTF-- +Fatal error: Attribute "Override" cannot target class alias (allowed targets: method) in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt index 6b994757a226e..a6abd649bd15e 100644 --- a/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt +++ b/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt @@ -20,6 +20,12 @@ array(1) { [0]=> object(ReflectionAttribute)#%d (1) { ["name"]=> - string(10) "ClassAlias" + string(19) "MyFunctionAttribute" } } + +Fatal error: Uncaught Error: Attribute "MyFunctionAttribute" cannot target class alias (allowed targets: function) in %s:%d +Stack trace: +#0 %s(%d): ReflectionAttribute->newInstance() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt index ea20f481db716..ce2abec6d3aec 100644 --- a/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt +++ b/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt @@ -7,6 +7,5 @@ Validation of attribute repetition (not allowed; internal attribute) class Demo {} ?> ---EXPECT-- - -e \ No newline at end of file +--EXPECTF-- +Fatal error: Attribute "Deprecated" must not be repeated in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt index 516a5243e55a4..5fdbbd8c687b4 100644 --- a/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt +++ b/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt @@ -15,10 +15,21 @@ $attributes[0]->newInstance(); ?> --EXPECTF-- -array(1) { +array(2) { [0]=> object(ReflectionAttribute)#%d (1) { ["name"]=> - string(10) "ClassAlias" + string(11) "MyAttribute" + } + [1]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(11) "MyAttribute" } } + +Fatal error: Uncaught Error: Attribute "MyAttribute" must not be repeated in %s:%d +Stack trace: +#0 %s(%d): ReflectionAttribute->newInstance() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt index aa2f6511e63f0..305af9358e29e 100644 --- a/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt +++ b/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt @@ -15,14 +15,15 @@ $attributes[0]->newInstance(); ?> --EXPECTF-- -array(1) { +array(2) { [0]=> object(ReflectionAttribute)#%d (1) { ["name"]=> - string(10) "ClassAlias" + string(11) "MyAttribute" + } + [1]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(11) "MyAttribute" } } -php: /usr/src/php/Zend/zend_variables.c:143: zval_copy_ctor_func: Assertion `!(zval_gc_flags(((*(zvalue)).value.str)->gc.u.type_info) & (1<<6))' failed. -Aborted (core dumped) - -Termsig=6 diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt index 22abb9504264c..00c30aec2d5dd 100644 --- a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt +++ b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt @@ -20,6 +20,6 @@ array(1) { [0]=> object(ReflectionAttribute)#%d (1) { ["name"]=> - string(10) "ClassAlias" + string(11) "MyAttribute" } } diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt index 15772aafe7bc0..7b11a76aef861 100644 --- a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt +++ b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt @@ -20,6 +20,6 @@ array(1) { [0]=> object(ReflectionAttribute)#%d (1) { ["name"]=> - string(10) "ClassAlias" + string(11) "MyAttribute" } } diff --git a/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt index 8a979095e7b95..dbc50b75f5566 100644 --- a/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt +++ b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt @@ -9,4 +9,4 @@ function demo( ?> --EXPECTF-- -Fatal error: Attribute "Deprecated" cannot target parameter (allowed targets: class, function, method, class constant, constant) in %s on line %d +Fatal error: Attribute "Deprecated" cannot target parameter (allowed targets: class, function, method, class constant, constant, class alias) in %s on line %d diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 990103099d428..0b449cf92c35a 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -23,6 +23,7 @@ #include "zend_attributes_arginfo.h" #include "zend_exceptions.h" #include "zend_smart_str.h" +#include "zend_class_alias.h" ZEND_API zend_class_entry *zend_ce_attribute; ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute; @@ -39,7 +40,121 @@ static zend_object_handlers attributes_object_handlers_sensitive_parameter_value static HashTable internal_attributes; -uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope) +// zend_compile.c not interface +zend_string *zend_resolve_class_name_ast(zend_ast *ast); + +// Based on zend_compile.c but not part of the interface +void compile_alias_attributes( + HashTable **attributes, zend_ast *ast +) /* {{{ */ { + zend_attribute *attr; + zend_internal_attribute *config; + + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t j; + + ZEND_ASSERT(ast->kind == ZEND_AST_ARRAY); + + for (uint32_t elem_idx = 0; elem_idx < list->children; elem_idx++) { + zend_ast *array_elem = list->child[elem_idx]; + ZEND_ASSERT(array_elem->kind == ZEND_AST_ARRAY_ELEM); + + zend_ast *array_content = array_elem->child[0]; + if (array_content->kind != ZEND_AST_NEW) { + zend_error_noreturn(E_COMPILE_ERROR, + "Attribute must be declared with `new`"); + } + + if (array_content->child[1] && + array_content->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot create Closure as attribute argument"); + } + + zend_string *name = zend_resolve_class_name_ast(array_content->child[0]); + zend_string *lcname = zend_string_tolower_ex(name, false); + zend_ast_list *args = array_content->child[1] ? zend_ast_get_list(array_content->child[1]) : NULL; + + config = zend_internal_attribute_get(lcname); + zend_string_release(lcname); + + uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES) + ? ZEND_ATTRIBUTE_STRICT_TYPES : 0; + attr = zend_add_attribute( + attributes, name, args ? args->children : 0, flags, 0, array_content->lineno); + zend_string_release(name); + + /* Populate arguments */ + if (!args) { + continue; + } + ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); + + bool uses_named_args = 0; + for (j = 0; j < args->children; j++) { + zend_ast **arg_ast_ptr = &args->child[j]; + zend_ast *arg_ast = *arg_ast_ptr; + + if (arg_ast->kind == ZEND_AST_UNPACK) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use unpacking in attribute argument list"); + } + + if (arg_ast->kind == ZEND_AST_NAMED_ARG) { + attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); + arg_ast_ptr = &arg_ast->child[1]; + uses_named_args = 1; + + for (uint32_t k = 0; k < j; k++) { + if (attr->args[k].name && + zend_string_equals(attr->args[k].name, attr->args[j].name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", + ZSTR_VAL(attr->args[j].name)); + } + } + } else if (uses_named_args) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use positional argument after named argument"); + } + + zend_const_expr_to_zval( + &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); + } + } + + if (*attributes != NULL) { + /* Validate attributes in a secondary loop (needed to detect repeated attributes). */ + ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) { + if (attr->offset != 0 || NULL == (config = zend_internal_attribute_get(attr->lcname))) { + continue; + } + + uint32_t target = ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS; + + if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) { + zend_string *location = zend_get_attribute_target_names(target); + zend_string *allowed = zend_get_attribute_target_names(config->flags); + + zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)", + ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed) + ); + } + + if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) { + if (zend_is_attribute_repeated(*attributes, attr)) { + zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name)); + } + } + + if (config->validator != NULL) { + config->validator(attr, target, CG(active_class_entry)); + } + } ZEND_HASH_FOREACH_END(); + } +} +/* }}} */ + +uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. if (attr->argc > 0) { @@ -71,7 +186,42 @@ uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_cla return ZEND_ATTRIBUTE_TARGET_ALL; } -static zend_string *validate_allow_dynamic_properties( +static zend_execute_data *setup_dummy_call_frame(zend_string *filename, zend_attribute *attribute_data) { + /* Set up dummy call frame that makes it look like the attribute was invoked + * from where it occurs in the code. */ + zend_function dummy_func; + zend_op *opline; + + memset(&dummy_func, 0, sizeof(zend_function)); + + zend_execute_data *call = zend_vm_stack_push_call_frame_ex( + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) + + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) + + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)), + 0, &dummy_func, 0, NULL); + + opline = (zend_op*)(call + 1); + memset(opline, 0, sizeof(zend_op)); + opline->opcode = ZEND_DO_FCALL; + opline->lineno = attribute_data->lineno; + + call->opline = opline; + call->call = NULL; + call->return_value = NULL; + call->func = (zend_function*)(call->opline + 1); + call->prev_execute_data = EG(current_execute_data); + + memset(call->func, 0, sizeof(zend_function)); + call->func->type = ZEND_USER_FUNCTION; + call->func->op_array.fn_flags = + attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0; + call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE; + call->func->op_array.filename = filename; + + return call; +} + +static void validate_allow_dynamic_properties( zend_attribute *attr, uint32_t target, zend_class_entry *scope) { ZEND_ASSERT(scope != NULL); @@ -133,41 +283,152 @@ static zend_string *validate_deprecated( static void validate_class_alias( zend_attribute *attr, uint32_t target, zend_class_entry *scope) { - zval alias_obj; - ZVAL_UNDEF(&alias_obj); - zend_result result = zend_get_attribute_object( - &alias_obj, - zend_ce_class_alias, - attr, - scope, - scope->info.user.filename + // Do NOT construct the attribute yet, that would require any of the + // attributes that are used to exist; at this point, access the alias name + // based on the arguments, and do our own validation + zend_execute_data *call = setup_dummy_call_frame(scope->info.user.filename, attr); + EG(current_execute_data) = call; + + zend_execute_data *constructor_call = zend_vm_stack_push_call_frame( + ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS, + zend_ce_class_alias->constructor, + attr->argc, + scope ); - if (result == FAILURE) { - ZEND_ASSERT(EG(exception)); + constructor_call->prev_execute_data = EG(current_execute_data); + EG(current_execute_data) = constructor_call; + + if (attr->argc < 1 || attr->argc > 2) { + zend_wrong_parameters_count_error(1, 2); + + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); return; } - zval *alias_name = zend_read_property( - zend_ce_class_alias, - Z_OBJ(alias_obj), - ZEND_STRL("alias"), - false, - NULL - ); - result = zend_register_class_alias_ex( - Z_STRVAL_P(alias_name), - Z_STRLEN_P(alias_name), + zval *found = NULL; + + // Looking for either the first parameter, or if the first parameter + // is named, the 'alias' parameter + if (attr->args[0].name == NULL) { + found = &( attr->args[0].value ); + } else { + for (uint32_t i = 0; i < attr->argc; i++) { + if (zend_string_equals_literal( attr->args[i].name, "alias")) { + found = &( attr->args[i].value ); + break; + } + } + if (found == NULL) { + zend_argument_error(zend_ce_argument_count_error, 1, "not passed"); + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + return; + } + } + + zend_string *alias; + if (UNEXPECTED(!zend_parse_arg_str(found, &alias, false, 0))) { + zend_wrong_parameter_error( + ZPP_ERROR_WRONG_ARG, + 1, + "alias", + Z_EXPECTED_STRING, + found + ); + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + return; + } + + zend_result result = zend_register_class_alias_ex( + ZSTR_VAL(alias), + ZSTR_LEN(alias), scope, false ); if (result == FAILURE) { zend_error_noreturn(E_ERROR, "Unable to declare alias '%s' for '%s'", - Z_STRVAL_P(alias_name), + ZSTR_VAL(alias), ZSTR_VAL(scope->name) ); + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + return; + } + + zend_string *lc_name; + if (ZSTR_VAL(alias)[0] == '\\') { + lc_name = zend_string_alloc(ZSTR_LEN(alias) - 1, 0); + zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(alias) + 1, ZSTR_LEN(alias) - 1); + } else { + lc_name = zend_string_tolower(alias); + } + + zval *entry = zend_hash_find(EG(class_table), lc_name); + zend_string_release_ex(lc_name, /* persistent */ false); + ZEND_ASSERT(entry != NULL); + ZEND_ASSERT(Z_TYPE_P(entry) == IS_ALIAS_PTR); + + zend_class_alias *alias_obj = Z_CLASS_ALIAS_P(entry); + + // Compile attributes + if (attr->argc == 2) { + zval *nested_attribs = NULL; + if (attr->args[0].name == NULL && attr->args[1].name == NULL) { + nested_attribs = &( attr->args[1].value ); + } else { + for (uint32_t i = 0; i < attr->argc; i++) { + if ( attr->args[i].name == NULL ) { + continue; + } + if (zend_string_equals_literal( attr->args[i].name, "alias")) { + continue; + } + if (zend_string_equals_literal( attr->args[i].name, "attributes")) { + nested_attribs = &( attr->args[i].value ); + break; + } + zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(attr->args[i].name)); + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + return; + } + } + ZEND_ASSERT(nested_attribs != NULL); + if (UNEXPECTED(!Z_OPT_CONSTANT_P(nested_attribs))) { + zend_wrong_parameter_error( + ZPP_ERROR_WRONG_ARG, + 2, + "attributes", + Z_EXPECTED_ARRAY, + nested_attribs + ); + // Something with an invalid parameter + } else { + zend_ast *attributes_ast = Z_ASTVAL_P(nested_attribs); + compile_alias_attributes(&( alias_obj->attributes), attributes_ast); + } } - zval_ptr_dtor(alias_name); - zval_ptr_dtor(&alias_obj); + + // zval_ptr_dtor(alias_name); + // zval_ptr_dtor(&alias_obj); + EG(current_execute_data) = constructor_call->prev_execute_data; + zend_vm_stack_free_call_frame(constructor_call); + EG(current_execute_data) = call->prev_execute_data; + zend_vm_stack_free_call_frame(call); + return; } ZEND_METHOD(Attribute, __construct) @@ -397,6 +658,10 @@ ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *a ZVAL_COPY_OR_DUP(ret, &attr->args[i].value); if (Z_TYPE_P(ret) == IS_CONSTANT_AST) { + // Delayed validation for attributes in class aliases + if (CG(in_compilation) && i == 1 && zend_string_equals(attr->name, zend_ce_class_alias->name)) { + return SUCCESS; + } if (SUCCESS != zval_update_constant_ex(ret, scope)) { zval_ptr_dtor(ret); return FAILURE; @@ -411,37 +676,7 @@ ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attr zend_execute_data *call = NULL; if (filename) { - /* Set up dummy call frame that makes it look like the attribute was invoked - * from where it occurs in the code. */ - zend_function dummy_func; - zend_op *opline; - - memset(&dummy_func, 0, sizeof(zend_function)); - - call = zend_vm_stack_push_call_frame_ex( - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) + - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) + - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)), - 0, &dummy_func, 0, NULL); - - opline = (zend_op*)(call + 1); - memset(opline, 0, sizeof(zend_op)); - opline->opcode = ZEND_DO_FCALL; - opline->lineno = attribute_data->lineno; - - call->opline = opline; - call->call = NULL; - call->return_value = NULL; - call->func = (zend_function*)(call->opline + 1); - call->prev_execute_data = EG(current_execute_data); - - memset(call->func, 0, sizeof(zend_function)); - call->func->type = ZEND_USER_FUNCTION; - call->func->op_array.fn_flags = - attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0; - call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE; - call->func->op_array.filename = filename; - + call = setup_dummy_call_frame(filename, attribute_data); EG(current_execute_data) = call; } @@ -676,17 +911,14 @@ void zend_register_attribute_ce(void) zend_ce_nodiscard = register_class_NoDiscard(); attr = zend_mark_internal_attribute(zend_ce_nodiscard); -<<<<<<< HEAD attr->validator = validate_nodiscard; zend_ce_delayed_target_validation = register_class_DelayedTargetValidation(); attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); -======= zend_ce_class_alias = register_class_ClassAlias(); attr = zend_mark_internal_attribute(zend_ce_class_alias); attr->validator = validate_class_alias; ->>>>>>> e1a37a8b7b7 (Add #[ClassAlias]) } void zend_attributes_shutdown(void) diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c index a8d4e0c6988cd..aab09760e41d5 100644 --- a/Zend/zend_class_alias.c +++ b/Zend/zend_class_alias.c @@ -19,11 +19,12 @@ #include "zend_class_alias.h" zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { - zend_class_alias *alias = malloc(sizeof(zend_class_alias)); + zend_class_alias *alias = malloc(sizeof(zend_class_alias)); // refcount field is only there for compatibility with other structures GC_SET_REFCOUNT(alias, 1); alias->ce = ce; + alias->attributes = NULL; return alias; } diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h index fdf0c61a6514c..1c3e62ca52377 100644 --- a/Zend/zend_class_alias.h +++ b/Zend/zend_class_alias.h @@ -24,6 +24,7 @@ struct _zend_class_alias { zend_refcounted_h gc; zend_class_entry *ce; + HashTable *attributes; }; typedef struct _zend_class_alias zend_class_alias; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index ee83ee75ff6ea..3c1ac56bd7428 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1222,7 +1222,7 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* } /* }}} */ -static zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ +zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ { const zval *class_name = zend_ast_get_zval(ast); if (Z_TYPE_P(class_name) != IS_STRING) { diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 24b480ad71e66..0d7ddaeb8e349 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -28,6 +28,7 @@ #include "zend_sort.h" #include "zend_constants.h" #include "zend_observer.h" +#include "zend_class_alias.h" #include "zend_vm.h" @@ -303,6 +304,20 @@ ZEND_API void zend_cleanup_mutable_class_data(zend_class_entry *ce) ZEND_API void destroy_zend_class(zval *zv) { + /* We don't increase the refcount for class aliases, + * so we don't need to destroy the underlying ->ce here, but we do need + * to free the attributes and the storage for the + * skip the destruction of aliases entirely. */ + if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) { + zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); + + if (class_alias->attributes) { + zend_hash_release(class_alias->attributes); + class_alias->attributes = NULL; + } + return; + } + zend_property_info *prop_info; zend_class_entry *ce = Z_PTR_P(zv); zend_function *fn; @@ -311,12 +326,6 @@ 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; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index fa82cc01ab7fd..bef02b4264fff 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -8183,6 +8183,7 @@ ZEND_METHOD(ReflectionClassAlias, __construct) } zval *entry = zend_hash_find(EG(class_table), lc_name); + zend_string_release_ex(lc_name, /* persistent */ false); ZEND_ASSERT(entry != NULL); if (Z_TYPE_P(entry) != IS_ALIAS_PTR) { @@ -8193,8 +8194,6 @@ ZEND_METHOD(ReflectionClassAlias, __construct) zend_class_alias *alias = Z_CLASS_ALIAS_P(entry); - zend_string_release_ex(lc_name, /* persistent */ false); - intern->ptr = alias; intern->ref_type = REF_TYPE_OTHER; @@ -8211,7 +8210,7 @@ ZEND_METHOD(ReflectionClassAlias, getAttributes) GET_REFLECTION_OBJECT_PTR(alias); reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, - NULL, 0, alias->ce, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS, + alias->attributes, 0, alias->ce, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS, NULL); } @@ -8225,8 +8224,12 @@ ZEND_METHOD(ReflectionClassAlias, __toString) GET_REFLECTION_OBJECT_PTR(alias); - smart_str_appends(&str, "TODO ReflectionClassAlias::__toString()"); - // _const_string(&str, ZSTR_VAL(const_->name), &const_->value, ""); + smart_str_append_printf( + &str, + "%s - alias for %s", + Z_STRVAL_P(reflection_prop_name(ZEND_THIS)), + ZSTR_VAL(alias->ce->name) + ); RETURN_STR(smart_str_extract(&str)); } diff --git a/ext/reflection/tests/ReflectionClassAlias_construct_missing.phpt b/ext/reflection/tests/ReflectionClassAlias_construct_missing.phpt new file mode 100644 index 0000000000000..daa661dd40d0d --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_construct_missing.phpt @@ -0,0 +1,13 @@ +--TEST-- +ReflectionClassAlias::__construct() - missing class +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ReflectionException: Class "missing" does not exist in %s:%d +Stack trace: +#0 %s(%d): ReflectionClassAlias->__construct('missing') +#1 {main} + thrown in %s on line %d diff --git a/ext/reflection/tests/ReflectionClassAlias_construct_non_alias.phpt b/ext/reflection/tests/ReflectionClassAlias_construct_non_alias.phpt new file mode 100644 index 0000000000000..6563bb921689f --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_construct_non_alias.phpt @@ -0,0 +1,13 @@ +--TEST-- +ReflectionClassAlias::__construct() - not an alias +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught ReflectionException: "ReflectionClassAlias" is not an alias in %s:%d +Stack trace: +#0 %s(%d): ReflectionClassAlias->__construct('ReflectionClass...') +#1 {main} + thrown in %s on line %d diff --git a/ext/reflection/tests/ReflectionClassAlias_construct_valid.phpt b/ext/reflection/tests/ReflectionClassAlias_construct_valid.phpt new file mode 100644 index 0000000000000..f0d02ad4be5a7 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_construct_valid.phpt @@ -0,0 +1,15 @@ +--TEST-- +ReflectionClassAlias::__construct() - with an alias +--FILE-- + +--EXPECTF-- +object(ReflectionClassAlias)#%d (1) { + ["name"]=> + string(22) "MyReflectionClassAlias" +} diff --git a/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt b/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt new file mode 100644 index 0000000000000..746c847abdc7b --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt @@ -0,0 +1,14 @@ +--TEST-- +ReflectionClassAlias::__toString() +--FILE-- +getAttributes() ); + +?> +--EXPECT-- +array(0) { +} diff --git a/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt b/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt new file mode 100644 index 0000000000000..1dcdcb1402cd0 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt @@ -0,0 +1,20 @@ +--TEST-- +ReflectionClassAlias::__toString() +--FILE-- +getAttributes() ); + +?> +--EXPECTF-- +array(1) { + [0]=> + object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(16) "MissingAttribute" + } +} diff --git a/ext/reflection/tests/ReflectionClassAlias_toString.phpt b/ext/reflection/tests/ReflectionClassAlias_toString.phpt new file mode 100644 index 0000000000000..20d2cc63cbbf0 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_toString.phpt @@ -0,0 +1,12 @@ +--TEST-- +ReflectionClassAlias::__toString() +--FILE-- + +--EXPECT-- +MyReflectionClassAlias - alias for ReflectionClassAlias From 858a9a2df6aba19b0b09674438252e4cdef72866 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:13:42 -0700 Subject: [PATCH 07/30] goto --- Zend/zend_attributes.c | 35 ++++++----------------------------- 1 file changed, 6 insertions(+), 29 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 0b449cf92c35a..b43d3f6b67fd1 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -301,12 +301,7 @@ static void validate_class_alias( if (attr->argc < 1 || attr->argc > 2) { zend_wrong_parameters_count_error(1, 2); - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); - return; + goto restore_execution_data; } zval *found = NULL; @@ -324,11 +319,7 @@ static void validate_class_alias( } if (found == NULL) { zend_argument_error(zend_ce_argument_count_error, 1, "not passed"); - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); - return; + goto restore_execution_data; } } @@ -341,11 +332,7 @@ static void validate_class_alias( Z_EXPECTED_STRING, found ); - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); - return; + goto restore_execution_data; } zend_result result = zend_register_class_alias_ex( @@ -359,11 +346,7 @@ static void validate_class_alias( ZSTR_VAL(alias), ZSTR_VAL(scope->name) ); - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); - return; + goto restore_execution_data; } zend_string *lc_name; @@ -399,11 +382,7 @@ static void validate_class_alias( break; } zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(attr->args[i].name)); - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); - return; + goto restore_execution_data; } } ZEND_ASSERT(nested_attribs != NULL); @@ -422,13 +401,11 @@ static void validate_class_alias( } } - // zval_ptr_dtor(alias_name); - // zval_ptr_dtor(&alias_obj); +restore_execution_data: EG(current_execute_data) = constructor_call->prev_execute_data; zend_vm_stack_free_call_frame(constructor_call); EG(current_execute_data) = call->prev_execute_data; zend_vm_stack_free_call_frame(call); - return; } ZEND_METHOD(Attribute, __construct) From 34c60099f9225baadd5c12d25faf71d54be9f3ed Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:22:48 -0700 Subject: [PATCH 08/30] zend_apply_internal_attribute_validation() --- Zend/zend_attributes.c | 51 +++++++++++++++++++++++------------------- Zend/zend_attributes.h | 2 ++ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index b43d3f6b67fd1..2c2396d38cf39 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -123,36 +123,41 @@ void compile_alias_attributes( } if (*attributes != NULL) { - /* Validate attributes in a secondary loop (needed to detect repeated attributes). */ - ZEND_HASH_PACKED_FOREACH_PTR(*attributes, attr) { - if (attr->offset != 0 || NULL == (config = zend_internal_attribute_get(attr->lcname))) { - continue; - } + zend_apply_internal_attribute_validation(*attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); + } +} +/* }}} */ - uint32_t target = ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS; +ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, uint32_t offset, uint32_t target) { + zend_attribute *attr; + zend_internal_attribute *config; - if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) { - zend_string *location = zend_get_attribute_target_names(target); - zend_string *allowed = zend_get_attribute_target_names(config->flags); + /* Validate attributes in a secondary loop (needed to detect repeated attributes). */ + ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) { + if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) { + continue; + } - zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)", - ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed) - ); - } + if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) { + zend_string *location = zend_get_attribute_target_names(target); + zend_string *allowed = zend_get_attribute_target_names(config->flags); - if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) { - if (zend_is_attribute_repeated(*attributes, attr)) { - zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name)); - } - } + zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)", + ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed) + ); + } - if (config->validator != NULL) { - config->validator(attr, target, CG(active_class_entry)); + if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) { + if (zend_is_attribute_repeated(attributes, attr)) { + zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name)); } - } ZEND_HASH_FOREACH_END(); - } + } + + if (config->validator != NULL) { + config->validator(attr, target, CG(active_class_entry)); + } + } ZEND_HASH_FOREACH_END(); } -/* }}} */ uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) { diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 218e4cb9d85e3..53baa5ef9f2fd 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -100,6 +100,8 @@ ZEND_API zend_attribute *zend_add_attribute( uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope); +ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, uint32_t offset, uint32_t target); + END_EXTERN_C() static zend_always_inline zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc) From a6c5b8f1c38400157ddd7c958c59809a09d928f4 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:32:03 -0700 Subject: [PATCH 09/30] zend_attribute_populate_arguments() --- Zend/zend_attributes.c | 77 ++++++++++++++++++++---------------------- Zend/zend_attributes.h | 1 + Zend/zend_compile.c | 33 +----------------- 3 files changed, 39 insertions(+), 72 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 2c2396d38cf39..2b8dd5d2e02e9 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -48,10 +48,8 @@ void compile_alias_attributes( HashTable **attributes, zend_ast *ast ) /* {{{ */ { zend_attribute *attr; - zend_internal_attribute *config; zend_ast_list *list = zend_ast_get_list(ast); - uint32_t j; ZEND_ASSERT(ast->kind == ZEND_AST_ARRAY); @@ -72,12 +70,8 @@ void compile_alias_attributes( } zend_string *name = zend_resolve_class_name_ast(array_content->child[0]); - zend_string *lcname = zend_string_tolower_ex(name, false); zend_ast_list *args = array_content->child[1] ? zend_ast_get_list(array_content->child[1]) : NULL; - config = zend_internal_attribute_get(lcname); - zend_string_release(lcname); - uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES) ? ZEND_ATTRIBUTE_STRICT_TYPES : 0; attr = zend_add_attribute( @@ -85,40 +79,8 @@ void compile_alias_attributes( zend_string_release(name); /* Populate arguments */ - if (!args) { - continue; - } - ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - - bool uses_named_args = 0; - for (j = 0; j < args->children; j++) { - zend_ast **arg_ast_ptr = &args->child[j]; - zend_ast *arg_ast = *arg_ast_ptr; - - if (arg_ast->kind == ZEND_AST_UNPACK) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use unpacking in attribute argument list"); - } - - if (arg_ast->kind == ZEND_AST_NAMED_ARG) { - attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); - arg_ast_ptr = &arg_ast->child[1]; - uses_named_args = 1; - - for (uint32_t k = 0; k < j; k++) { - if (attr->args[k].name && - zend_string_equals(attr->args[k].name, attr->args[j].name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", - ZSTR_VAL(attr->args[j].name)); - } - } - } else if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use positional argument after named argument"); - } - - zend_const_expr_to_zval( - &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); + if (args) { + zend_attribute_populate_arguments(attr, args); } } @@ -159,6 +121,41 @@ ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, ui } ZEND_HASH_FOREACH_END(); } +ZEND_API void zend_attribute_populate_arguments(zend_attribute *attr, zend_ast_list *args) { + ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); + + bool uses_named_args = 0; + for (uint32_t j = 0; j < args->children; j++) { + zend_ast **arg_ast_ptr = &args->child[j]; + zend_ast *arg_ast = *arg_ast_ptr; + + if (arg_ast->kind == ZEND_AST_UNPACK) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use unpacking in attribute argument list"); + } + + if (arg_ast->kind == ZEND_AST_NAMED_ARG) { + attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); + arg_ast_ptr = &arg_ast->child[1]; + uses_named_args = 1; + + for (uint32_t k = 0; k < j; k++) { + if (attr->args[k].name && + zend_string_equals(attr->args[k].name, attr->args[j].name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", + ZSTR_VAL(attr->args[j].name)); + } + } + } else if (uses_named_args) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use positional argument after named argument"); + } + + zend_const_expr_to_zval( + &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); + } +} + uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 53baa5ef9f2fd..12a4ed24ad32c 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -101,6 +101,7 @@ ZEND_API zend_attribute *zend_add_attribute( uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope); ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, uint32_t offset, uint32_t target); +ZEND_API void zend_attribute_populate_arguments(zend_attribute *attr, zend_ast_list *args); END_EXTERN_C() diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3c1ac56bd7428..1b964c4873e2e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7833,38 +7833,7 @@ static void zend_compile_attributes( /* Populate arguments */ if (args) { - ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - - bool uses_named_args = false; - for (j = 0; j < args->children; j++) { - zend_ast **arg_ast_ptr = &args->child[j]; - zend_ast *arg_ast = *arg_ast_ptr; - - if (arg_ast->kind == ZEND_AST_UNPACK) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use unpacking in attribute argument list"); - } - - if (arg_ast->kind == ZEND_AST_NAMED_ARG) { - attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); - arg_ast_ptr = &arg_ast->child[1]; - uses_named_args = true; - - for (uint32_t k = 0; k < j; k++) { - if (attr->args[k].name && - zend_string_equals(attr->args[k].name, attr->args[j].name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", - ZSTR_VAL(attr->args[j].name)); - } - } - } else if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use positional argument after named argument"); - } - - zend_const_expr_to_zval( - &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); - } + zend_attribute_populate_arguments(attr, args); } } } From 270d2f63073a413c0f17e50a06ae505696e1f70c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:44:50 -0700 Subject: [PATCH 10/30] Fixes from tests --- Zend/Optimizer/zend_optimizer.c | 20 ++++++++++++++----- .../ReflectionExtension_getClasses_basic.phpt | 7 ++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index f8cbefdaaf2b5..401d5ce41d509 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -30,6 +30,7 @@ #include "zend_call_graph.h" #include "zend_inference.h" #include "zend_dump.h" +#include "zend_class_alias.h" #include "php.h" #ifndef ZEND_OPTIMIZER_MAX_REGISTERED_PASSES @@ -776,7 +777,8 @@ void zend_optimizer_shift_jump(const zend_op_array *op_array, zend_op *opline, c static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename) { - const zend_class_entry *ce = Z_PTR_P(ce_zv); + const zend_class_entry *ce; + Z_CE_FROM_ZVAL_P(ce, ce_zv); if (ce->ce_flags & ZEND_ACC_PRELOADED) { if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { @@ -818,14 +820,22 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, const zend_string *file zend_class_entry *zend_optimizer_get_class_entry( const zend_script *script, const zend_op_array *op_array, zend_string *lcname) { - zend_class_entry *ce = script ? zend_hash_find_ptr(&script->class_table, lcname) : NULL; - if (ce) { - return ce; + zval *ce_or_alias = script ? zend_hash_find(&script->class_table, lcname) : NULL; + if (ce_or_alias) { + if (EXPECTED(Z_TYPE_P(ce_or_alias) == IS_PTR)) { + return Z_PTR_P(ce_or_alias); + } + ZEND_ASSERT(Z_TYPE_P(ce_or_alias) == IS_ALIAS_PTR); + return Z_CLASS_ALIAS_P(ce_or_alias)->ce; } zval *ce_zv = zend_hash_find(CG(class_table), lcname); if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array ? op_array->filename : NULL)) { - return Z_PTR_P(ce_zv); + if (EXPECTED(Z_TYPE_P(ce_zv) == IS_PTR)) { + return Z_PTR_P(ce_zv); + } + ZEND_ASSERT(Z_TYPE_P(ce_zv) == IS_ALIAS_PTR); + return Z_CLASS_ALIAS_P(ce_zv)->ce; } if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) { diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 8ba243a503bd0..e665446699326 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECTF-- -array(26) { +array(27) { ["ReflectionException"]=> object(ReflectionClass)#%d (1) { ["name"]=> @@ -139,4 +139,9 @@ array(26) { ["name"]=> string(16) "PropertyHookType" } + ["ReflectionClassAlias"]=> + object(ReflectionClass)#%d (1) { + ["name"]=> + string(20) "ReflectionClassAlias" + } } From 16541e64f44a69e24a273004f4336f7c363a1a9c Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 01:45:43 -0700 Subject: [PATCH 11/30] Fix windows file --- win32/build/config.w32 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 0f233d86bae5e..a9626aef626d0 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -241,7 +241,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_float.c zend_string.c zend_generators.c zend_virtual_cwd.c zend_ast.c \ zend_inheritance.c zend_smart_str.c zend_cpuinfo.c zend_observer.c zend_system_id.c \ zend_enum.c zend_fibers.c zend_atomic.c zend_hrtime.c zend_frameless_function.c zend_property_hooks.c \ - zend_lazy_objects.c zend_autoload.c zend_class_alias.h"); + zend_lazy_objects.c zend_autoload.c zend_class_alias.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({ From b4561db44d77f7b43506f6e92f0dcc3a29edd1c0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 14:07:16 -0700 Subject: [PATCH 12/30] Add deprecation Not yet wired to emit anything yet though --- Zend/zend_attributes.c | 10 ++++++++++ Zend/zend_class_alias.c | 1 + Zend/zend_class_alias.h | 1 + ext/reflection/php_reflection.c | 14 +++++++++++++- ext/reflection/php_reflection.stub.php | 2 ++ ext/reflection/php_reflection_arginfo.h | 6 +++++- ext/reflection/php_reflection_decl.h | 8 ++++---- .../ReflectionClassAlias_isDeprecated.phpt | 18 ++++++++++++++++++ ...flectionClassAlias_toString_deprecated.phpt | 13 +++++++++++++ 9 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt create mode 100644 ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 2b8dd5d2e02e9..271744eb23a1d 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -400,6 +400,16 @@ static void validate_class_alias( } else { zend_ast *attributes_ast = Z_ASTVAL_P(nested_attribs); compile_alias_attributes(&( alias_obj->attributes), attributes_ast); + + zend_attribute *deprecated_attribute = zend_get_attribute_str( + alias_obj->attributes, + "deprecated", + strlen("deprecated") + ); + + if (deprecated_attribute) { + alias_obj->alias_flags |= ZEND_ACC_DEPRECATED; + } } } diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c index aab09760e41d5..2994732fb0c77 100644 --- a/Zend/zend_class_alias.c +++ b/Zend/zend_class_alias.c @@ -25,6 +25,7 @@ zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { alias->ce = ce; alias->attributes = NULL; + alias->alias_flags = 0; return alias; } diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h index 1c3e62ca52377..42741c7146357 100644 --- a/Zend/zend_class_alias.h +++ b/Zend/zend_class_alias.h @@ -25,6 +25,7 @@ struct _zend_class_alias { zend_refcounted_h gc; zend_class_entry *ce; HashTable *attributes; + uint32_t alias_flags; }; typedef struct _zend_class_alias zend_class_alias; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index bef02b4264fff..332945f1eb153 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -8226,13 +8226,25 @@ ZEND_METHOD(ReflectionClassAlias, __toString) smart_str_append_printf( &str, - "%s - alias for %s", + "%s - %salias for %s", Z_STRVAL_P(reflection_prop_name(ZEND_THIS)), + (alias->alias_flags & ZEND_ACC_DEPRECATED) ? "deprecated " : "", ZSTR_VAL(alias->ce->name) ); RETURN_STR(smart_str_extract(&str)); } +ZEND_METHOD(ReflectionClassAlias, isDeprecated) +{ + reflection_object *intern; + zend_class_alias *alias; + + ZEND_PARSE_PARAMETERS_NONE(); + + GET_REFLECTION_OBJECT_PTR(alias); + RETURN_BOOL(alias->alias_flags & ZEND_ACC_DEPRECATED); +} + PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index bf88a2791daa4..00b9727d9e067 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -952,5 +952,7 @@ public function __construct(string $name) {} public function __toString(): string {} + public function isDeprecated(): bool {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 1f39d0cc2d17f..76aa6d93d843c 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 6e955854a092f91f1331acabcd564537a58ff62f + * Stub hash: 360dcefc11527b30c94cc59f8475a25a8a0a90ca * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) @@ -737,6 +737,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassAlias___toString arginfo_class_ReflectionFunction___toString +#define arginfo_class_ReflectionClassAlias_isDeprecated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType + #define arginfo_class_ReflectionClassAlias_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes ZEND_METHOD(Reflection, getModifierNames); @@ -1014,6 +1016,7 @@ ZEND_METHOD(ReflectionConstant, __toString); ZEND_METHOD(ReflectionConstant, getAttributes); ZEND_METHOD(ReflectionClassAlias, __construct); ZEND_METHOD(ReflectionClassAlias, __toString); +ZEND_METHOD(ReflectionClassAlias, isDeprecated); ZEND_METHOD(ReflectionClassAlias, getAttributes); static const zend_function_entry class_Reflection_methods[] = { @@ -1394,6 +1397,7 @@ static const zend_function_entry class_ReflectionConstant_methods[] = { static const zend_function_entry class_ReflectionClassAlias_methods[] = { ZEND_ME(ReflectionClassAlias, __construct, arginfo_class_ReflectionClassAlias___construct, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassAlias, __toString, arginfo_class_ReflectionClassAlias___toString, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassAlias, isDeprecated, arginfo_class_ReflectionClassAlias_isDeprecated, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassAlias, getAttributes, arginfo_class_ReflectionClassAlias_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/php_reflection_decl.h b/ext/reflection/php_reflection_decl.h index d86ee8d9c6f52..bb7594cc7ad1d 100644 --- a/ext/reflection/php_reflection_decl.h +++ b/ext/reflection/php_reflection_decl.h @@ -1,12 +1,12 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 6e955854a092f91f1331acabcd564537a58ff62f */ + * Stub hash: 360dcefc11527b30c94cc59f8475a25a8a0a90ca */ -#ifndef ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H -#define ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H +#ifndef ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H +#define ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H typedef enum zend_enum_PropertyHookType { ZEND_ENUM_PropertyHookType_Get = 1, ZEND_ENUM_PropertyHookType_Set = 2, } zend_enum_PropertyHookType; -#endif /* ZEND_PHP_REFLECTION_DECL_6e955854a092f91f1331acabcd564537a58ff62f_H */ +#endif /* ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H */ diff --git a/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt b/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt new file mode 100644 index 0000000000000..4c27321782692 --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt @@ -0,0 +1,18 @@ +--TEST-- +ReflectionClassAlias::isDeprecated() +--FILE-- +isDeprecated() ); + +$r = new ReflectionClassAlias( 'NewAlias' ); +var_dump( $r->isDeprecated() ); +?> +--EXPECT-- +bool(true) +bool(false) diff --git a/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt b/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt new file mode 100644 index 0000000000000..656cf874bb24e --- /dev/null +++ b/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt @@ -0,0 +1,13 @@ +--TEST-- +ReflectionClassAlias::__toString() for a deprecated alias +--FILE-- + +--EXPECT-- +Other - deprecated alias for Demo From 138f8cfafb17853b0d69c93d5642062879087b0f Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 14:25:30 -0700 Subject: [PATCH 13/30] Autoload with zvals --- Zend/zend_execute.h | 2 +- Zend/zend_execute_API.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_execute.h b/Zend/zend_execute.h index 89a9e79143a82..f2a23bb4865d5 100644 --- a/Zend/zend_execute.h +++ b/Zend/zend_execute.h @@ -35,7 +35,7 @@ ZEND_API extern void (*zend_execute_ex)(zend_execute_data *execute_data); ZEND_API extern void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value); /* The lc_name may be stack allocated! */ -ZEND_API extern zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name); +ZEND_API extern zval *(*zend_autoload)(zend_string *name, zend_string *lc_name); void init_executor(void); void shutdown_executor(void); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index e9ed58cc200ff..7cad735afe39d 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -52,7 +52,7 @@ ZEND_API void (*zend_execute_ex)(zend_execute_data *execute_data); ZEND_API void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value); -ZEND_API zend_class_entry *(*zend_autoload)(zend_string *name, zend_string *lc_name); +ZEND_API zval *(*zend_autoload)(zend_string *name, zend_string *lc_name); #ifdef ZEND_WIN32 ZEND_TLS HANDLE tq_timer = NULL; From 98c10c6085742b254e89f380e595f0b9e26ad25a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 15:00:48 -0700 Subject: [PATCH 14/30] Emit deprecation warnings! --- .../class_alias/access_constant.phpt | 16 +++++++++ .../class_alias/access_static_prop.phpt | 15 +++++++++ .../class_alias/call_static_method.phpt | 18 ++++++++++ .../deprecated/class_alias/messages.phpt | 33 +++++++++++++++++++ .../class_alias/use_constructor.phpt | 18 ++++++++++ Zend/zend_class_alias.c | 19 +++++++++++ Zend/zend_class_alias.h | 1 + Zend/zend_compile.c | 25 ++++++++++++-- Zend/zend_execute.c | 2 +- Zend/zend_execute_API.c | 29 +++++++++++++++- ext/reflection/php_reflection.c | 5 +-- 11 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 Zend/tests/attributes/deprecated/class_alias/access_constant.phpt create mode 100644 Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt create mode 100644 Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt create mode 100644 Zend/tests/attributes/deprecated/class_alias/messages.phpt create mode 100644 Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt diff --git a/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt b/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt new file mode 100644 index 0000000000000..d2db12119459c --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt @@ -0,0 +1,16 @@ +--TEST-- +#[\Deprecated]: Class alias - access a constant +--FILE-- + +--EXPECTF-- +Deprecated: Alias is deprecated in %s on line %d +int(1) diff --git a/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt b/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt new file mode 100644 index 0000000000000..3354b3e9bd22b --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt @@ -0,0 +1,15 @@ +--TEST-- +#[\Deprecated]: Class alias - access a static variable +--FILE-- + +--EXPECTF-- +Deprecated: Alias is deprecated in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt b/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt new file mode 100644 index 0000000000000..ae5fe3b34e1d3 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\Deprecated]: Class alias - call a static method +--FILE-- + +--EXPECTF-- +Deprecated: Alias is deprecated in %s on line %d +Clazz::doNothing diff --git a/Zend/tests/attributes/deprecated/class_alias/messages.phpt b/Zend/tests/attributes/deprecated/class_alias/messages.phpt new file mode 100644 index 0000000000000..3c9477d2552f6 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_alias/messages.phpt @@ -0,0 +1,33 @@ +--TEST-- +#[\Deprecated]: Class alias - all messages have details +--FILE-- + +--EXPECTF-- +Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d +int(1) + +Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d + +Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d + +Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt b/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt new file mode 100644 index 0000000000000..1338880e5f1e1 --- /dev/null +++ b/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt @@ -0,0 +1,18 @@ +--TEST-- +#[\Deprecated]: Class alias - use the constructor +--FILE-- + +--EXPECTF-- +Deprecated: Alias is deprecated in %s on line %d +Clazz::__construct diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c index 2994732fb0c77..d3d105071271b 100644 --- a/Zend/zend_class_alias.c +++ b/Zend/zend_class_alias.c @@ -17,6 +17,9 @@ */ #include "zend_class_alias.h" +#include "zend_errors.h" +#include "zend_string.h" +#include "zend.h" zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { zend_class_alias *alias = malloc(sizeof(zend_class_alias)); @@ -29,3 +32,19 @@ zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { return alias; } + +ZEND_COLD zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix); + +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_alias(const zend_class_alias *alias) { + zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); + + if (get_deprecation_suffix_from_attribute(alias->attributes, NULL, &message_suffix) == FAILURE) { + return; + } + + zend_error_unchecked(E_USER_DEPRECATED, "Alias is deprecated%S", + message_suffix + ); + + zend_string_release(message_suffix); +} diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h index 42741c7146357..b8d1fdc333d4f 100644 --- a/Zend/zend_class_alias.h +++ b/Zend/zend_class_alias.h @@ -51,5 +51,6 @@ typedef struct _zend_class_alias zend_class_alias; zend_class_alias * zend_class_alias_init(zend_class_entry *ce); +ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_alias(const zend_class_alias *alias); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 1b964c4873e2e..65559602b5f1e 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -39,6 +39,7 @@ #include "zend_call_stack.h" #include "zend_frameless_function.h" #include "zend_property_hooks.h" +#include "zend_class_alias.h" #define SET_NODE(target, src) do { \ target ## _type = (src)->op_type; \ @@ -1885,8 +1886,23 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend if (class_name_refers_to_active_ce(class_name, fetch_type)) { cc = zend_hash_find_ptr(&CG(active_class_entry)->constants_table, name); } else if (fetch_type == ZEND_FETCH_CLASS_DEFAULT && !(CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION)) { - const zend_class_entry *ce = zend_hash_find_ptr_lc(CG(class_table), class_name); - if (ce) { + zend_string *lc_key = zend_string_tolower(class_name); + zval *ce_or_alias = zend_hash_find(CG(class_table), lc_key); + zend_string_release(lc_key); + + if (ce_or_alias) { + zend_class_entry *ce; + if (Z_TYPE_P(ce_or_alias) == IS_ALIAS_PTR) { + zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_or_alias); + if (alias->alias_flags & ZEND_ACC_DEPRECATED) { + // Cannot evaluate at compile time + return 0; + } + ce = alias->ce; + } else { + ZEND_ASSERT(Z_TYPE_P(ce_or_alias) == IS_PTR); + ce = Z_PTR_P(ce_or_alias); + } cc = zend_hash_find_ptr(&ce->constants_table, name); } else { return 0; @@ -5634,7 +5650,10 @@ static void zend_compile_static_call(znode *result, zend_ast *ast, uint32_t type zend_class_entry *ce = NULL; if (opline->op1_type == IS_CONST) { zend_string *lcname = Z_STR_P(CT_CONSTANT(opline->op1) + 1); - ce = zend_hash_find_ptr(CG(class_table), lcname); + zval *ce_or_alias = zend_hash_find(CG(class_table), lcname); + if (ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); + } if (ce) { if (zend_compile_ignore_class(ce, CG(active_op_array)->filename)) { ce = NULL; diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 37278c5cb9a23..4ee4b503af2de 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1798,7 +1798,7 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void) zend_throw_error(NULL, "%s", msg); } -ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) +ZEND_COLD zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) { *message_suffix = ZSTR_EMPTY_ALLOC(); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 7cad735afe39d..aa5bf7b1e4a90 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1201,6 +1201,11 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_string_release_ex(lc_name, 0); } Z_CE_FROM_ZVAL_P(ce, zv); + zend_class_alias *alias = NULL; + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + ce_cache = 0; + alias = Z_CLASS_ALIAS_P(zv); + } if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && @@ -1210,6 +1215,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_hash_init(CG(unlinked_uses), 0, NULL, NULL, 0); } zend_hash_index_add_empty_element(CG(unlinked_uses), (zend_long)(uintptr_t)ce); + if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { + zend_deprecated_class_alias(alias); + } return ce; } return NULL; @@ -1220,6 +1228,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { SET_CE_CACHE(ce_cache, ce); } + if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { + zend_deprecated_class_alias(alias); + } return ce; } @@ -1261,7 +1272,20 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_long previous_lineno = EG(lineno_override); EG(filename_override) = NULL; EG(lineno_override) = -1; - ce = zend_autoload(autoload_name, lc_name); + zend_exception_save(); + zval *ce_zval = zend_autoload(autoload_name, lc_name); + zend_class_alias *alias = NULL; + if (ce_zval) { + if (Z_TYPE_P(ce_zval) == IS_ALIAS_PTR) { + ce_cache = 0; + alias = Z_CLASS_ALIAS_P(ce_zval); + ce = alias->ce; + } else { + ZEND_ASSERT(Z_TYPE_P(ce_zval) == IS_PTR); + ce = Z_PTR_P(ce_zval); + } + } + zend_exception_restore(); EG(filename_override) = previous_filename; EG(lineno_override) = previous_lineno; @@ -1277,6 +1301,9 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * SET_CE_CACHE(ce_cache, ce); } } + if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { + zend_deprecated_class_alias(alias); + } return ce; } /* }}} */ diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 332945f1eb153..0f47bccc51bac 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -8164,8 +8164,9 @@ ZEND_METHOD(ReflectionClassAlias, __construct) ZEND_PARSE_PARAMETERS_END(); // First use zend_lookup_class() which will also take care of autoloading, - // but that will always return the underlying class entry - zend_class_entry *ce = zend_lookup_class(name); + // but that will always return the underlying class entry; don't complain + // about deprecations here + zend_class_entry *ce = zend_lookup_class_ex(name, NULL, ZEND_FETCH_CLASS_SILENT); if (ce == NULL) { if (!EG(exception)) { zend_throw_exception_ex(reflection_exception_ptr, -1, "Class \"%s\" does not exist", ZSTR_VAL(name)); From 11a3eb694fd98bff2454e277733acee0f3e8fe31 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 15:08:28 -0700 Subject: [PATCH 15/30] Names in deprecation warnings --- .../deprecated/class_alias/access_constant.phpt | 2 +- .../deprecated/class_alias/access_static_prop.phpt | 2 +- .../deprecated/class_alias/call_static_method.phpt | 2 +- .../tests/attributes/deprecated/class_alias/messages.phpt | 8 ++++---- .../deprecated/class_alias/use_constructor.phpt | 2 +- Zend/zend_API.c | 3 +++ Zend/zend_class_alias.c | 5 ++++- Zend/zend_class_alias.h | 1 + Zend/zend_opcode.c | 4 ++++ 9 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt b/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt index d2db12119459c..8b3885d964204 100644 --- a/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt +++ b/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt @@ -12,5 +12,5 @@ var_dump(MyAlias::TEST); ?> --EXPECTF-- -Deprecated: Alias is deprecated in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d int(1) diff --git a/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt b/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt index 3354b3e9bd22b..530c92125a9bf 100644 --- a/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt +++ b/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt @@ -12,4 +12,4 @@ MyAlias::$v = true; ?> --EXPECTF-- -Deprecated: Alias is deprecated in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt b/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt index ae5fe3b34e1d3..bbaa89215bc8c 100644 --- a/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt +++ b/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt @@ -14,5 +14,5 @@ MyAlias::doNothing(); ?> --EXPECTF-- -Deprecated: Alias is deprecated in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d Clazz::doNothing diff --git a/Zend/tests/attributes/deprecated/class_alias/messages.phpt b/Zend/tests/attributes/deprecated/class_alias/messages.phpt index 3c9477d2552f6..08ff0fdda74e7 100644 --- a/Zend/tests/attributes/deprecated/class_alias/messages.phpt +++ b/Zend/tests/attributes/deprecated/class_alias/messages.phpt @@ -23,11 +23,11 @@ MyAlias::doNothing(); ?> --EXPECTF-- -Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d int(1) -Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d -Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d -Deprecated: Alias is deprecated since 8.4, don't use the alias in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt b/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt index 1338880e5f1e1..1e09c3a0b7cfd 100644 --- a/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt +++ b/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt @@ -14,5 +14,5 @@ $o = new MyAlias(); ?> --EXPECTF-- -Deprecated: Alias is deprecated in %s on line %d +Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d Clazz::__construct diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5ed6d2cb85626..f7253780b8e7c 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3617,6 +3617,9 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ */ zend_class_alias *alias = zend_class_alias_init(ce); + zend_string *original_name = zend_string_init(name, name_len, persistent); + alias->name = original_name; + ZVAL_ALIAS_PTR(&zv, alias); ret = zend_hash_add(CG(class_table), lcname, &zv); diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c index d3d105071271b..7a2db3fe47b11 100644 --- a/Zend/zend_class_alias.c +++ b/Zend/zend_class_alias.c @@ -27,6 +27,7 @@ zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { GC_SET_REFCOUNT(alias, 1); alias->ce = ce; + alias->name = NULL; alias->attributes = NULL; alias->alias_flags = 0; @@ -42,7 +43,9 @@ ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_alias(const zend_cla return; } - zend_error_unchecked(E_USER_DEPRECATED, "Alias is deprecated%S", + zend_error_unchecked(E_USER_DEPRECATED, "Alias %s for class %s is deprecated%S", + ZSTR_VAL(alias->name), + ZSTR_VAL(alias->ce->name), message_suffix ); diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h index b8d1fdc333d4f..6ca62eb2fa940 100644 --- a/Zend/zend_class_alias.h +++ b/Zend/zend_class_alias.h @@ -24,6 +24,7 @@ struct _zend_class_alias { zend_refcounted_h gc; zend_class_entry *ce; + zend_string *name; HashTable *attributes; uint32_t alias_flags; }; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0d7ddaeb8e349..d6ab111e520dd 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -315,6 +315,10 @@ ZEND_API void destroy_zend_class(zval *zv) zend_hash_release(class_alias->attributes); class_alias->attributes = NULL; } + if (class_alias->name) { + zend_string_release(class_alias->name); + class_alias->name = NULL; + } return; } From 3912c857c31d9bc02008da15de195dbc382fae39 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 22:22:52 -0700 Subject: [PATCH 16/30] Fixes --- Zend/Optimizer/zend_optimizer.c | 13 +++++++++++++ Zend/zend_API.c | 2 ++ Zend/zend_execute_API.c | 3 +++ Zend/zend_opcode.c | 4 ++-- ext/opcache/ZendAccelerator.c | 8 ++++++++ ext/opcache/jit/zend_jit.c | 4 +++- ext/opcache/zend_accelerator_util_funcs.c | 18 +++++++++++------- ext/opcache/zend_file_cache.c | 2 ++ ext/opcache/zend_persist.c | 23 ++++++++++++++++++++++- ext/opcache/zend_persist_calc.c | 5 +++++ 10 files changed, 71 insertions(+), 11 deletions(-) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 401d5ce41d509..900e6bb83f3c6 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -790,6 +790,13 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename return false; } } + // Ignore deprecated aliases so that they are fetched at runtime + if (Z_TYPE_P(ce_zv) == IS_ALIAS_PTR) { + zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_zv); + if (alias->alias_flags & ZEND_ACC_DEPRECATED) { + return true; + } + } return ce->type == ZEND_USER_CLASS && (!ce->info.user.filename || ce->info.user.filename != filename); } @@ -826,6 +833,12 @@ zend_class_entry *zend_optimizer_get_class_entry( return Z_PTR_P(ce_or_alias); } ZEND_ASSERT(Z_TYPE_P(ce_or_alias) == IS_ALIAS_PTR); + zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_or_alias); + if (alias->alias_flags & ZEND_ACC_DEPRECATED) { + // Pretend that the class cannot be found so that it gets looked + // up at runtime + return NULL; + } return Z_CLASS_ALIAS_P(ce_or_alias)->ce; } diff --git a/Zend/zend_API.c b/Zend/zend_API.c index f7253780b8e7c..dc93e45ce48e0 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3631,6 +3631,8 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ } return SUCCESS; } + + zend_string_release(original_name); return FAILURE; } /* }}} */ diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index aa5bf7b1e4a90..429949af3742a 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -327,6 +327,9 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + continue; + } zend_class_entry *ce; Z_CE_FROM_ZVAL_P(ce, zv); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index d6ab111e520dd..c4408bbb6c59c 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -313,11 +313,11 @@ ZEND_API void destroy_zend_class(zval *zv) if (class_alias->attributes) { zend_hash_release(class_alias->attributes); - class_alias->attributes = NULL; + // class_alias->attributes = NULL; } if (class_alias->name) { zend_string_release(class_alias->name); - class_alias->name = NULL; + // class_alias->name = NULL; } return; } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 8ea018eabc2d6..43973025f2163 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4183,6 +4183,14 @@ static void preload_link(void) continue; } + // Not preloading class constants for deprecated aliases + if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); + if (class_alias->alias_flags & ZEND_ACC_DEPRECATED) { + continue; + } + } + if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */ CG(in_compilation) = true; /* prevent autoloading */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index 3ffb669e84742..c30aa9c48e84c 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -575,7 +575,9 @@ static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, con ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); class_name = Z_STR_P(zv); - ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); + // ZEND_FETCH_CLASS_SILENT - ignore alias deprecation + // TODO but we want to not cache the ce if i is a deprecated alias + ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { ce = NULL; } diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index f99a80ff4e55c..6546f1543a139 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -354,16 +354,20 @@ static void zend_accel_do_delayed_early_binding( CG(compiled_filename) = persistent_script->script.filename; CG(in_compilation) = 1; for (uint32_t i = 0; i < persistent_script->num_early_bindings; i++) { - const zend_early_binding *early_binding = &persistent_script->early_bindings[i]; - zend_class_entry *ce = zend_hash_find_ex_ptr(EG(class_table), early_binding->lcname, 1); - if (!ce) { + zend_early_binding *early_binding = &persistent_script->early_bindings[i]; + zval *ce_or_alias = zend_hash_find_ex(EG(class_table), early_binding->lcname, 1); + if (!ce_or_alias) { zval *zv = zend_hash_find_known_hash(EG(class_table), early_binding->rtd_key); + zend_class_entry *ce = NULL; if (zv) { - zend_class_entry *orig_ce = Z_CE_P(zv); - zend_class_entry *parent_ce = !(orig_ce->ce_flags & ZEND_ACC_LINKED) - ? zend_hash_find_ex_ptr(EG(class_table), early_binding->lc_parent_name, 1) + zend_class_entry *orig_ce; + Z_CE_FROM_ZVAL_P(orig_ce, zv); + zval *parent_ce_or_alias = !(orig_ce->ce_flags & ZEND_ACC_LINKED) + ? zend_hash_find_ex(EG(class_table), early_binding->lc_parent_name, 1) : NULL; - if (parent_ce || (orig_ce->ce_flags & ZEND_ACC_LINKED)) { + if (parent_ce_or_alias || (orig_ce->ce_flags & ZEND_ACC_LINKED)) { + zend_class_entry *parent_ce; + Z_CE_FROM_ZVAL_P(parent_ce, parent_ce_or_alias); ce = zend_try_early_bind(orig_ce, parent_ce, early_binding->lcname, zv); } } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 3114e5b92712c..43978a6912a3e 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -788,6 +788,7 @@ static void zend_file_cache_serialize_class(zval *zv, zend_file_cache_metainfo *info, void *buf) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zend_class_entry *ce; SERIALIZE_PTR(Z_PTR_P(zv)); @@ -1647,6 +1648,7 @@ static void zend_file_cache_unserialize_class_constant(zval * zend_persistent_script *script, void *buf) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); if (!IS_UNSERIALIZED(Z_PTR_P(zv))) { zend_class_constant *c; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 9bc2496837ce4..4c9613a788481 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -29,6 +29,7 @@ #include "zend_operators.h" #include "zend_interfaces.h" #include "zend_attributes.h" +#include "zend_class_alias.h" #ifdef HAVE_JIT # include "Optimizer/zend_func_info.h" @@ -913,6 +914,21 @@ static void zend_persist_class_constant(zval *zv) zend_persist_type(&c->type); } +zend_class_alias *zend_persist_class_alias_entry(zend_class_alias *orig_alias) +{ + Bucket *p; + zend_class_alias *alias = orig_alias; + + alias = zend_shared_memdup_put(alias, sizeof(zend_class_alias)); + alias->ce = zend_persist_class_entry(alias->ce); + // zend_accel_store_string(alias->name); + if (alias->attributes) { + alias->attributes = zend_persist_attributes(alias->attributes); + } + + return alias; +} + zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) { Bucket *p; @@ -1334,7 +1350,12 @@ static void zend_accel_persist_class_table(HashTable *class_table) ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { ZEND_ASSERT(p->key != NULL); zend_accel_store_interned_string(p->key); - Z_CE(p->val) = zend_persist_class_entry(Z_CE(p->val)); + if (Z_TYPE(p->val) == IS_ALIAS_PTR) { + Z_CLASS_ALIAS(p->val) = zend_persist_class_alias_entry(Z_CLASS_ALIAS(p->val)); + } else { + ZEND_ASSERT(Z_TYPE(p->val) == IS_PTR); + Z_CE(p->val) = zend_persist_class_entry(Z_CE(p->val)); + } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_FOREACH_BUCKET(class_table, p) { if (EXPECTED(Z_TYPE(p->val) != IS_ALIAS_PTR)) { diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 98f7ad3324e12..271fc00118604 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -602,6 +602,11 @@ static void zend_persist_class_alias_entry_calc(const zend_class_alias *alias) // alias->ce is going to be a pointer to a class entry that will be // persisted on its own, here we just need to add size for the alias ADD_SIZE(sizeof(zend_class_alias)); + // And the things that the alias holds directly + ADD_INTERNED_STRING(alias->name); + if (alias->attributes) { + zend_persist_attributes_calc(alias->attributes); + } zend_persist_class_entry_calc(alias->ce); } From b713a6ac48a7e7182433398d7c09ce224b1ee6d1 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 7 Jun 2025 22:26:55 -0700 Subject: [PATCH 17/30] Unused variable --- ext/opcache/zend_persist.c | 1 - 1 file changed, 1 deletion(-) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 4c9613a788481..f478899a4dc34 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -916,7 +916,6 @@ static void zend_persist_class_constant(zval *zv) zend_class_alias *zend_persist_class_alias_entry(zend_class_alias *orig_alias) { - Bucket *p; zend_class_alias *alias = orig_alias; alias = zend_shared_memdup_put(alias, sizeof(zend_class_alias)); From 8cc3ac3bfaf4b319c445814158d60851ec6e0cdb Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 9 Jun 2025 16:30:30 -0700 Subject: [PATCH 18/30] Missing free --- Zend/zend_opcode.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index c4408bbb6c59c..88d12923bf2d8 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -319,6 +319,7 @@ ZEND_API void destroy_zend_class(zval *zv) zend_string_release(class_alias->name); // class_alias->name = NULL; } + free(class_alias); return; } From a2246d5640c95d5c32e112a0aba1c883e6548545 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 9 Jun 2025 16:30:40 -0700 Subject: [PATCH 19/30] More tests --- .../attributes_array_contains_non-object.phpt | 17 ++++++++++++ .../attributes_array_non-object.phpt | 17 ++++++++++++ ...runtime_attributes_validation_missing.phpt | 26 +++++++++++++++++++ ...e_attributes_validation_non-attribute.phpt | 26 +++++++++++++++++++ Zend/zend_attributes.c | 25 ++++++++++++------ 5 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt create mode 100644 Zend/tests/attributes/class_alias/attributes_array_non-object.phpt create mode 100644 Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt create mode 100644 Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt diff --git a/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt b/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt new file mode 100644 index 0000000000000..4da3ef4c723a7 --- /dev/null +++ b/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[ClassAlias] - attributes parameter validated at compile time (non-object in array) +--FILE-- +getAttributes()[0]; +var_dump( $attrib ); + +$attrib->newInstance(); + +?> +--EXPECTF-- +Fatal error: Attribute must be declared with `new` in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt b/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt new file mode 100644 index 0000000000000..12ecda3574ca0 --- /dev/null +++ b/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt @@ -0,0 +1,17 @@ +--TEST-- +#[ClassAlias] - attributes parameter validated at compile time (array can be evaluated) +--FILE-- +getAttributes()[0]; +var_dump( $attrib ); + +$attrib->newInstance(); + +?> +--EXPECTF-- +Fatal error: ClassAlias::__construct(): Argument #2 ($attributes) must be an array of objects in %s on line %d diff --git a/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt b/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt new file mode 100644 index 0000000000000..4f1e2412b66ea --- /dev/null +++ b/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt @@ -0,0 +1,26 @@ +--TEST-- +#[ClassAlias] - attributes parameter validated at runtime (missing class) +--FILE-- +getAttributes()[0]; +var_dump( $attrib ); + +$attrib->newInstance(); + +?> +--EXPECTF-- +object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" +} + +Fatal error: Uncaught Error: Class "MissingAttribute" not found in %s:%d +Stack trace: +#0 %s(%d): ReflectionAttribute->newInstance() +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt b/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt new file mode 100644 index 0000000000000..aae32fd975a4e --- /dev/null +++ b/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt @@ -0,0 +1,26 @@ +--TEST-- +#[ClassAlias] - attributes parameter value types not validated when constructing ClassAlias +--FILE-- +getAttributes()[0]; +var_dump( $attrib ); + +var_dump( $attrib->newInstance() ); + +?> +--EXPECTF-- +object(ReflectionAttribute)#%d (1) { + ["name"]=> + string(10) "ClassAlias" +} +object(ClassAlias)#%d (1) { + ["alias"]=> + string(5) "Other" +} diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 271744eb23a1d..86a82fdf62ac0 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -389,14 +389,23 @@ static void validate_class_alias( } ZEND_ASSERT(nested_attribs != NULL); if (UNEXPECTED(!Z_OPT_CONSTANT_P(nested_attribs))) { - zend_wrong_parameter_error( - ZPP_ERROR_WRONG_ARG, - 2, - "attributes", - Z_EXPECTED_ARRAY, - nested_attribs - ); - // Something with an invalid parameter + // If it is an array, then it must be an array that can be evaluated + // already + if (Z_TYPE_P(nested_attribs) == IS_ARRAY) { + zend_argument_type_error( + 2, + "must be an array of objects" + ); + } else { + zend_wrong_parameter_error( + ZPP_ERROR_WRONG_ARG, + 2, + "attributes", + Z_EXPECTED_ARRAY, + nested_attribs + ); + // Something with an invalid parameter + } } else { zend_ast *attributes_ast = Z_ASTVAL_P(nested_attribs); compile_alias_attributes(&( alias_obj->attributes), attributes_ast); From 3c663718c781cae5bf69c6d9626d70942fe100a3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 9 Jun 2025 16:44:55 -0700 Subject: [PATCH 20/30] Store attributes --- .../runtime_attributes_validation_non-attribute.phpt | 8 +++++++- Zend/zend_attributes.c | 4 ++++ Zend/zend_attributes.stub.php | 2 ++ Zend/zend_attributes_arginfo.h | 8 +++++++- 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt b/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt index aae32fd975a4e..b95f9929a4754 100644 --- a/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt +++ b/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt @@ -20,7 +20,13 @@ object(ReflectionAttribute)#%d (1) { ["name"]=> string(10) "ClassAlias" } -object(ClassAlias)#%d (1) { +object(ClassAlias)#%d (2) { ["alias"]=> string(5) "Other" + ["attributes"]=> + array(1) { + [0]=> + object(NonAttribute)#%d (0) { + } + } } diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 86a82fdf62ac0..015b6fb029716 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -586,6 +586,8 @@ ZEND_METHOD(ClassAlias, __construct) } if (attributes == NULL || zend_hash_num_elements(attributes) == 0) { + ZVAL_EMPTY_ARRAY(&value); + zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("attributes"), &value); return; } @@ -595,6 +597,8 @@ ZEND_METHOD(ClassAlias, __construct) ); RETURN_THROWS(); } + ZVAL_ARR(&value, attributes); + zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("attributes"), &value); } static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index 6dc09de2fba26..8ad621311d115 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -114,5 +114,7 @@ final class ClassAlias { public readonly string $alias; + public readonly array $attributes; + public function __construct(string $alias, array $attributes = []) {} } diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index a4a46e2ad4895..e31c72544f4b1 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: b70c1fed22c60805e01f5ec7f34ad8133ac70b28 */ + * Stub hash: 9f03d6d181126bf0d2116708ad1cdc2476ab46fd */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -322,6 +322,12 @@ static zend_class_entry *register_class_ClassAlias(void) zend_declare_typed_property(class_entry, property_alias_name, &property_alias_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release_ex(property_alias_name, true); + zval property_attributes_default_value; + ZVAL_UNDEF(&property_attributes_default_value); + zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); + zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); + zend_string_release_ex(property_attributes_name, true); + zend_string *attribute_name_Attribute_class_ClassAlias_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); zend_attribute *attribute_Attribute_class_ClassAlias_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ClassAlias_0, 1); zend_string_release_ex(attribute_name_Attribute_class_ClassAlias_0, true); From e8614caf694922d734c84ef298947602da4cea65 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 9 Jun 2025 19:37:12 -0700 Subject: [PATCH 21/30] Try --- Zend/Optimizer/zend_optimizer.c | 11 ++++++++--- .../attributes/class_alias/attributes_dict.phpt | 2 ++ Zend/zend_API.c | 1 + Zend/zend_attributes.c | 9 +++++---- Zend/zend_execute_API.c | 7 ++++--- Zend/zend_opcode.c | 4 ++++ ext/opcache/ZendAccelerator.c | 13 +++++++++---- ext/opcache/zend_file_cache.c | 1 + ext/opcache/zend_persist.c | 6 ++++++ 9 files changed, 40 insertions(+), 14 deletions(-) diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 900e6bb83f3c6..f24cf311e90f6 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -779,6 +779,9 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename { const zend_class_entry *ce; Z_CE_FROM_ZVAL_P(ce, ce_zv); + if (Z_TYPE_P(ce_zv) == IS_ALIAS_PTR) { + return true; + } if (ce->ce_flags & ZEND_ACC_PRELOADED) { if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { @@ -839,7 +842,8 @@ zend_class_entry *zend_optimizer_get_class_entry( // up at runtime return NULL; } - return Z_CLASS_ALIAS_P(ce_or_alias)->ce; + return NULL; + // return Z_CLASS_ALIAS_P(ce_or_alias)->ce; } zval *ce_zv = zend_hash_find(CG(class_table), lcname); @@ -848,7 +852,8 @@ zend_class_entry *zend_optimizer_get_class_entry( return Z_PTR_P(ce_zv); } ZEND_ASSERT(Z_TYPE_P(ce_zv) == IS_ALIAS_PTR); - return Z_CLASS_ALIAS_P(ce_zv)->ce; + return NULL; + // return Z_CLASS_ALIAS_P(ce_zv)->ce; } if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) { @@ -891,7 +896,7 @@ const zend_class_constant *zend_fetch_class_const_info( } else { zval *ce_zv = zend_hash_find(EG(class_table), Z_STR_P(op1 + 1)); if (ce_zv && !zend_optimizer_ignore_class(ce_zv, op_array->filename)) { - ce = Z_PTR_P(ce_zv); + Z_CE_FROM_ZVAL_P(ce, ce_zv); } } } diff --git a/Zend/tests/attributes/class_alias/attributes_dict.phpt b/Zend/tests/attributes/class_alias/attributes_dict.phpt index 0d09e3d0bd29c..7d582370e3c90 100644 --- a/Zend/tests/attributes/class_alias/attributes_dict.phpt +++ b/Zend/tests/attributes/class_alias/attributes_dict.phpt @@ -6,6 +6,8 @@ Alias attributes must not be associative #[ClassAlias('Other', ['test' => new Deprecated()])] class Demo {} +class_alias( 'Demo', 'Other2' ); + $attr = new ReflectionClass( Demo::class )->getAttributes()[0]; $attr->newInstance(); diff --git a/Zend/zend_API.c b/Zend/zend_API.c index dc93e45ce48e0..071e83aa52ace 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3633,6 +3633,7 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ } zend_string_release(original_name); + free(alias); return FAILURE; } /* }}} */ diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 015b6fb029716..1dac53dc950c8 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -337,6 +337,11 @@ static void validate_class_alias( goto restore_execution_data; } + // if (CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION) { + // // Opcache, don't register the alias + // goto restore_execution_data; + // } + zend_result result = zend_register_class_alias_ex( ZSTR_VAL(alias), ZSTR_LEN(alias), @@ -660,10 +665,6 @@ ZEND_API zend_result zend_get_attribute_value(zval *ret, const zend_attribute *a ZVAL_COPY_OR_DUP(ret, &attr->args[i].value); if (Z_TYPE_P(ret) == IS_CONSTANT_AST) { - // Delayed validation for attributes in class aliases - if (CG(in_compilation) && i == 1 && zend_string_equals(attr->name, zend_ce_class_alias->name)) { - return SUCCESS; - } if (SUCCESS != zval_update_constant_ex(ret, scope)) { zval_ptr_dtor(ret); return FAILURE; diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 429949af3742a..396149d3b341a 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -327,9 +327,10 @@ ZEND_API void zend_shutdown_executor_values(bool fast_shutdown) } } ZEND_HASH_FOREACH_END(); ZEND_HASH_MAP_REVERSE_FOREACH_VAL(EG(class_table), zv) { - if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { - continue; - } + // CHECK + // if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + // continue; + // } zend_class_entry *ce; Z_CE_FROM_ZVAL_P(ce, zv); diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 88d12923bf2d8..0b5e13b2295b1 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -311,6 +311,10 @@ ZEND_API void destroy_zend_class(zval *zv) if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) { zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); + if (class_alias->alias_flags & ZEND_ACC_IMMUTABLE) { + return; + } + if (class_alias->attributes) { zend_hash_release(class_alias->attributes); // class_alias->attributes = NULL; diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index 43973025f2163..d8d090a71de99 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -686,9 +686,16 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int /* class table hash keys, class names, properties, methods, constants, etc */ ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) { - zend_class_entry *ce; + zend_class_entry *ce = NULL; - Z_CE_FROM_ZVAL(ce, p->val); + if (EXPECTED(Z_TYPE(p->val) == IS_PTR)) { + ce = Z_PTR(p->val); + } else { + ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR); + zend_class_alias *alias = Z_PTR(p->val); + alias->name = new_interned_string(alias->name); + ce = alias->ce; + } if (p->key) { p->key = new_interned_string(p->key); @@ -4141,8 +4148,6 @@ static void preload_link(void) zend_hash_index_del( CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv)); } - zend_hash_index_del( - CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv)); } /* Restore the original class. */ diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 43978a6912a3e..f86e6e274cb3e 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -1674,6 +1674,7 @@ static void zend_file_cache_unserialize_class(zval *zv, zend_persistent_script *script, void *buf) { + ZEND_ASSERT(Z_TYPE_P(zv) == IS_PTR); zend_class_entry *ce; UNSERIALIZE_PTR(Z_PTR_P(zv)); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index f478899a4dc34..c0fca27171106 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -925,6 +925,12 @@ zend_class_alias *zend_persist_class_alias_entry(zend_class_alias *orig_alias) alias->attributes = zend_persist_attributes(alias->attributes); } + if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { + alias->alias_flags |= ZEND_ACC_IMMUTABLE; + } else { + alias->alias_flags |= ZEND_ACC_FILE_CACHED; + } + return alias; } From 7d8b2214fbd4544581a65d16e15d570aec8ca8b7 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Sat, 14 Jun 2025 22:08:08 -0700 Subject: [PATCH 22/30] Revert attributes/name/etc. Just switching to a zend_class_alias object should be easier --- Zend/Optimizer/zend_optimizer.c | 22 +- Zend/tests/attributes/034_target_values.phpt | 24 +- .../attributes_array_contains_non-object.phpt | 17 - .../attributes_array_non-object.phpt | 17 - .../class_alias/attributes_dict.phpt | 21 - .../class_alias/attributes_nonarray.phpt | 14 - .../class_alias/attributes_valid.phpt | 12 - .../attributes/class_alias/name_invalid.phpt | 11 - .../class_alias/name_nonstring.phpt | 11 - .../attributes/class_alias/name_required.phpt | 15 - .../class_alias/redeclaration_error.phpt | 11 - .../class_alias/repeated_attribute.phpt | 25 -- ...runtime_attributes_validation_missing.phpt | 26 -- ...e_attributes_validation_non-attribute.phpt | 32 -- .../class_alias/target_validation.phpt | 11 - .../class_alias/working_example.phpt | 17 - ...class_alias_listed_as_target-internal.phpt | 11 - ...class_alias_listed_as_target-userland.phpt | 31 -- .../must_target_class_alias-internal.phpt | 11 - .../must_target_class_alias-userland.phpt | 31 -- .../not_repeatable-internal.phpt | 11 - .../not_repeatable-userland.phpt | 35 -- .../repeatable-internal.phpt | 15 - .../repeatable-userland.phpt | 29 -- ...arget_all_targets_class_alias-default.phpt | 25 -- ...rget_all_targets_class_alias-explicit.phpt | 25 -- .../constant_listed_as_target-internal.phpt | 2 +- .../class_alias/access_constant.phpt | 16 - .../class_alias/access_static_prop.phpt | 15 - .../class_alias/call_static_method.phpt | 18 - .../deprecated/class_alias/messages.phpt | 33 -- .../class_alias/use_constructor.phpt | 18 - Zend/zend_API.c | 4 - Zend/zend_attributes.c | 380 ++---------------- Zend/zend_attributes.h | 10 +- Zend/zend_attributes.stub.php | 16 +- Zend/zend_attributes_arginfo.h | 48 +-- Zend/zend_class_alias.c | 22 - Zend/zend_class_alias.h | 3 - Zend/zend_compile.c | 47 ++- Zend/zend_execute.c | 2 +- Zend/zend_execute_API.c | 14 - Zend/zend_opcode.c | 8 - ext/opcache/ZendAccelerator.c | 18 +- ext/opcache/jit/zend_jit.c | 4 +- ext/opcache/zend_persist.c | 4 - ext/opcache/zend_persist_calc.c | 5 - ext/reflection/php_reflection.c | 26 +- ext/reflection/php_reflection.stub.php | 4 - ext/reflection/php_reflection_arginfo.h | 10 +- ext/reflection/php_reflection_decl.h | 8 +- ...lectionClassAlias_getAttributes_empty.phpt | 14 - ...ionClassAlias_getAttributes_non-empty.phpt | 20 - .../ReflectionClassAlias_isDeprecated.phpt | 18 - ...lectionClassAlias_toString_deprecated.phpt | 13 - 55 files changed, 95 insertions(+), 1215 deletions(-) delete mode 100644 Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt delete mode 100644 Zend/tests/attributes/class_alias/attributes_array_non-object.phpt delete mode 100644 Zend/tests/attributes/class_alias/attributes_dict.phpt delete mode 100644 Zend/tests/attributes/class_alias/attributes_nonarray.phpt delete mode 100644 Zend/tests/attributes/class_alias/attributes_valid.phpt delete mode 100644 Zend/tests/attributes/class_alias/name_invalid.phpt delete mode 100644 Zend/tests/attributes/class_alias/name_nonstring.phpt delete mode 100644 Zend/tests/attributes/class_alias/name_required.phpt delete mode 100644 Zend/tests/attributes/class_alias/redeclaration_error.phpt delete mode 100644 Zend/tests/attributes/class_alias/repeated_attribute.phpt delete mode 100644 Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt delete mode 100644 Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt delete mode 100644 Zend/tests/attributes/class_alias/target_validation.phpt delete mode 100644 Zend/tests/attributes/class_alias/working_example.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/repeatable-internal.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/repeatable-userland.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt delete mode 100644 Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt delete mode 100644 Zend/tests/attributes/deprecated/class_alias/access_constant.phpt delete mode 100644 Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt delete mode 100644 Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt delete mode 100644 Zend/tests/attributes/deprecated/class_alias/messages.phpt delete mode 100644 Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt delete mode 100644 ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt delete mode 100644 ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt delete mode 100644 ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt delete mode 100644 ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index f24cf311e90f6..e22888cf6c2bf 100644 --- a/Zend/Optimizer/zend_optimizer.c +++ b/Zend/Optimizer/zend_optimizer.c @@ -779,9 +779,6 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename { const zend_class_entry *ce; Z_CE_FROM_ZVAL_P(ce, ce_zv); - if (Z_TYPE_P(ce_zv) == IS_ALIAS_PTR) { - return true; - } if (ce->ce_flags & ZEND_ACC_PRELOADED) { if (CG(compiler_options) & ZEND_COMPILE_WITH_FILE_CACHE) { @@ -793,13 +790,6 @@ static bool zend_optimizer_ignore_class(zval *ce_zv, const zend_string *filename return false; } } - // Ignore deprecated aliases so that they are fetched at runtime - if (Z_TYPE_P(ce_zv) == IS_ALIAS_PTR) { - zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_zv); - if (alias->alias_flags & ZEND_ACC_DEPRECATED) { - return true; - } - } return ce->type == ZEND_USER_CLASS && (!ce->info.user.filename || ce->info.user.filename != filename); } @@ -836,14 +826,7 @@ zend_class_entry *zend_optimizer_get_class_entry( return Z_PTR_P(ce_or_alias); } ZEND_ASSERT(Z_TYPE_P(ce_or_alias) == IS_ALIAS_PTR); - zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_or_alias); - if (alias->alias_flags & ZEND_ACC_DEPRECATED) { - // Pretend that the class cannot be found so that it gets looked - // up at runtime - return NULL; - } - return NULL; - // return Z_CLASS_ALIAS_P(ce_or_alias)->ce; + return Z_CLASS_ALIAS_P(ce_or_alias)->ce; } zval *ce_zv = zend_hash_find(CG(class_table), lcname); @@ -852,8 +835,7 @@ zend_class_entry *zend_optimizer_get_class_entry( return Z_PTR_P(ce_zv); } ZEND_ASSERT(Z_TYPE_P(ce_zv) == IS_ALIAS_PTR); - return NULL; - // return Z_CLASS_ALIAS_P(ce_zv)->ce; + return Z_CLASS_ALIAS_P(ce_zv)->ce; } if (op_array && op_array->scope && zend_string_equals_ci(op_array->scope->name, lcname)) { diff --git a/Zend/tests/attributes/034_target_values.phpt b/Zend/tests/attributes/034_target_values.phpt index 2a06d39ebf05e..e56c0c285fbd6 100644 --- a/Zend/tests/attributes/034_target_values.phpt +++ b/Zend/tests/attributes/034_target_values.phpt @@ -16,26 +16,24 @@ showFlag("TARGET_PROPERTY", Attribute::TARGET_PROPERTY); showFlag("TARGET_CLASS_CONSTANT", Attribute::TARGET_CLASS_CONSTANT); showFlag("TARGET_PARAMETER", Attribute::TARGET_PARAMETER); showFlag("TARGET_CONSTANT", Attribute::TARGET_CONSTANT); -showFlag("TARGET_CLASS_ALIAS", Attribute::TARGET_CLASS_ALIAS); showFlag("IS_REPEATABLE", Attribute::IS_REPEATABLE); $all = Attribute::TARGET_CLASS | Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD | Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS_CONSTANT | Attribute::TARGET_PARAMETER - | Attribute::TARGET_CONSTANT | Attribute::TARGET_CLASS_ALIAS; + | Attribute::TARGET_CONSTANT; var_dump($all, Attribute::TARGET_ALL, $all === Attribute::TARGET_ALL); ?> --EXPECT-- -Attribute::TARGET_CLASS = 1 (255 & 1 === 1) -Attribute::TARGET_FUNCTION = 2 (255 & 2 === 2) -Attribute::TARGET_METHOD = 4 (255 & 4 === 4) -Attribute::TARGET_PROPERTY = 8 (255 & 8 === 8) -Attribute::TARGET_CLASS_CONSTANT = 16 (255 & 16 === 16) -Attribute::TARGET_PARAMETER = 32 (255 & 32 === 32) -Attribute::TARGET_CONSTANT = 64 (255 & 64 === 64) -Attribute::TARGET_CLASS_ALIAS = 128 (255 & 128 === 128) -Attribute::IS_REPEATABLE = 256 (255 & 256 === 0) -int(255) -int(255) +Attribute::TARGET_CLASS = 1 (127 & 1 === 1) +Attribute::TARGET_FUNCTION = 2 (127 & 2 === 2) +Attribute::TARGET_METHOD = 4 (127 & 4 === 4) +Attribute::TARGET_PROPERTY = 8 (127 & 8 === 8) +Attribute::TARGET_CLASS_CONSTANT = 16 (127 & 16 === 16) +Attribute::TARGET_PARAMETER = 32 (127 & 32 === 32) +Attribute::TARGET_CONSTANT = 64 (127 & 64 === 64) +Attribute::IS_REPEATABLE = 128 (127 & 128 === 0) +int(127) +int(127) bool(true) diff --git a/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt b/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt deleted file mode 100644 index 4da3ef4c723a7..0000000000000 --- a/Zend/tests/attributes/class_alias/attributes_array_contains_non-object.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -#[ClassAlias] - attributes parameter validated at compile time (non-object in array) ---FILE-- -getAttributes()[0]; -var_dump( $attrib ); - -$attrib->newInstance(); - -?> ---EXPECTF-- -Fatal error: Attribute must be declared with `new` in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt b/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt deleted file mode 100644 index 12ecda3574ca0..0000000000000 --- a/Zend/tests/attributes/class_alias/attributes_array_non-object.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -#[ClassAlias] - attributes parameter validated at compile time (array can be evaluated) ---FILE-- -getAttributes()[0]; -var_dump( $attrib ); - -$attrib->newInstance(); - -?> ---EXPECTF-- -Fatal error: ClassAlias::__construct(): Argument #2 ($attributes) must be an array of objects in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_dict.phpt b/Zend/tests/attributes/class_alias/attributes_dict.phpt deleted file mode 100644 index 7d582370e3c90..0000000000000 --- a/Zend/tests/attributes/class_alias/attributes_dict.phpt +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -Alias attributes must not be associative ---FILE-- - new Deprecated()])] -class Demo {} - -class_alias( 'Demo', 'Other2' ); - -$attr = new ReflectionClass( Demo::class )->getAttributes()[0]; -$attr->newInstance(); - -?> ---EXPECTF-- -Fatal error: Uncaught Error: ClassAlias::__construct(): Argument #2 ($attributes) must be a list, not an associative array in %s:%d -Stack trace: -#0 %s(%d): ClassAlias->__construct('Other', Array) -#1 %s(%d): ReflectionAttribute->newInstance() -#2 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_nonarray.phpt b/Zend/tests/attributes/class_alias/attributes_nonarray.phpt deleted file mode 100644 index a0513c9fc7a8c..0000000000000 --- a/Zend/tests/attributes/class_alias/attributes_nonarray.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -Alias attributes must be an array ---FILE-- -getAttributes()[0]; -$attr->newInstance(); - -?> ---EXPECTF-- -Fatal error: ClassAlias::__construct(): Argument #2 ($attributes) must be of type array, true given in %s on line %d diff --git a/Zend/tests/attributes/class_alias/attributes_valid.phpt b/Zend/tests/attributes/class_alias/attributes_valid.phpt deleted file mode 100644 index 8b790c57ae062..0000000000000 --- a/Zend/tests/attributes/class_alias/attributes_valid.phpt +++ /dev/null @@ -1,12 +0,0 @@ ---TEST-- -Alias attributes do not need to exist for declaration ---FILE-- - ---EXPECT-- -Done diff --git a/Zend/tests/attributes/class_alias/name_invalid.phpt b/Zend/tests/attributes/class_alias/name_invalid.phpt deleted file mode 100644 index 028c31414b41e..0000000000000 --- a/Zend/tests/attributes/class_alias/name_invalid.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Alias name must be valid ---FILE-- - ---EXPECTF-- -Fatal error: Cannot use "never" as a class alias as it is reserved in %s on line %d diff --git a/Zend/tests/attributes/class_alias/name_nonstring.phpt b/Zend/tests/attributes/class_alias/name_nonstring.phpt deleted file mode 100644 index 6be25a38e61b9..0000000000000 --- a/Zend/tests/attributes/class_alias/name_nonstring.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Parameter must be a string ---FILE-- - ---EXPECTF-- -Fatal error: ClassAlias::__construct(): Argument #1 ($alias) must be of type string, array given in %s on line %d diff --git a/Zend/tests/attributes/class_alias/name_required.phpt b/Zend/tests/attributes/class_alias/name_required.phpt deleted file mode 100644 index 2f907925adc4f..0000000000000 --- a/Zend/tests/attributes/class_alias/name_required.phpt +++ /dev/null @@ -1,15 +0,0 @@ ---TEST-- -Parameter is required ---FILE-- - ---EXPECTF-- -Fatal error: Uncaught ArgumentCountError: ClassAlias::__construct() expects at least 1 argument, 0 given in %s:%d -Stack trace: -#0 %s(%d): ClassAlias->__construct() -#1 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/redeclaration_error.phpt b/Zend/tests/attributes/class_alias/redeclaration_error.phpt deleted file mode 100644 index 642d08760a645..0000000000000 --- a/Zend/tests/attributes/class_alias/redeclaration_error.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Cannot redeclare an existing class ---FILE-- - ---EXPECTF-- -Fatal error: Unable to declare alias 'Attribute' for 'Demo' in %s on line %d diff --git a/Zend/tests/attributes/class_alias/repeated_attribute.phpt b/Zend/tests/attributes/class_alias/repeated_attribute.phpt deleted file mode 100644 index f2b6fa1617868..0000000000000 --- a/Zend/tests/attributes/class_alias/repeated_attribute.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -Attribute can be repeated ---FILE-- - ---EXPECTF-- -bool(true) -bool(true) -object(Demo)#%d (0) { -} -object(Demo)#%d (0) { -} diff --git a/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt b/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt deleted file mode 100644 index 4f1e2412b66ea..0000000000000 --- a/Zend/tests/attributes/class_alias/runtime_attributes_validation_missing.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -#[ClassAlias] - attributes parameter validated at runtime (missing class) ---FILE-- -getAttributes()[0]; -var_dump( $attrib ); - -$attrib->newInstance(); - -?> ---EXPECTF-- -object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(10) "ClassAlias" -} - -Fatal error: Uncaught Error: Class "MissingAttribute" not found in %s:%d -Stack trace: -#0 %s(%d): ReflectionAttribute->newInstance() -#1 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt b/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt deleted file mode 100644 index b95f9929a4754..0000000000000 --- a/Zend/tests/attributes/class_alias/runtime_attributes_validation_non-attribute.phpt +++ /dev/null @@ -1,32 +0,0 @@ ---TEST-- -#[ClassAlias] - attributes parameter value types not validated when constructing ClassAlias ---FILE-- -getAttributes()[0]; -var_dump( $attrib ); - -var_dump( $attrib->newInstance() ); - -?> ---EXPECTF-- -object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(10) "ClassAlias" -} -object(ClassAlias)#%d (2) { - ["alias"]=> - string(5) "Other" - ["attributes"]=> - array(1) { - [0]=> - object(NonAttribute)#%d (0) { - } - } -} diff --git a/Zend/tests/attributes/class_alias/target_validation.phpt b/Zend/tests/attributes/class_alias/target_validation.phpt deleted file mode 100644 index 4dbf6fadc6ce1..0000000000000 --- a/Zend/tests/attributes/class_alias/target_validation.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Can only be used on classes ---FILE-- - ---EXPECTF-- -Fatal error: Attribute "ClassAlias" cannot target function (allowed targets: class) in %s on line %d diff --git a/Zend/tests/attributes/class_alias/working_example.phpt b/Zend/tests/attributes/class_alias/working_example.phpt deleted file mode 100644 index ea1ae4476f395..0000000000000 --- a/Zend/tests/attributes/class_alias/working_example.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -Working usage ---FILE-- - ---EXPECTF-- -bool(true) -object(Demo)#%d (0) { -} diff --git a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt deleted file mode 100644 index 4f3bbb6fcb0af..0000000000000 --- a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-internal.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Class alias listed in valid targets when used wrong (internal attribute) ---FILE-- - ---EXPECTF-- -Fatal error: Attribute "Deprecated" cannot target class (allowed targets: function, method, class constant, constant, class alias) in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt b/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt deleted file mode 100644 index cccedd532a2ab..0000000000000 --- a/Zend/tests/attributes/class_alias_target/class_alias_listed_as_target-userland.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -Class alias listed in valid targets when used wrong (userland attribute) ---FILE-- -getAttributes(); -var_dump($attribs); -$attribs[0]->newInstance(); - -?> ---EXPECTF-- -array(1) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(16) "MyAliasAttribute" - } -} - -Fatal error: Uncaught Error: Attribute "MyAliasAttribute" cannot target class (allowed targets: class alias) in %s:%d -Stack trace: -#0 %s(%d): ReflectionAttribute->newInstance() -#1 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt deleted file mode 100644 index 2e741bea2208a..0000000000000 --- a/Zend/tests/attributes/class_alias_target/must_target_class_alias-internal.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Error when attribute does not target class alias (internal attribute) ---FILE-- - ---EXPECTF-- -Fatal error: Attribute "Override" cannot target class alias (allowed targets: method) in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt b/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt deleted file mode 100644 index a6abd649bd15e..0000000000000 --- a/Zend/tests/attributes/class_alias_target/must_target_class_alias-userland.phpt +++ /dev/null @@ -1,31 +0,0 @@ ---TEST-- -Error when attribute does not target class alias (useland attribute) ---FILE-- -getAttributes(); -var_dump($attribs); -$attribs[0]->newInstance(); - -?> ---EXPECTF-- -array(1) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(19) "MyFunctionAttribute" - } -} - -Fatal error: Uncaught Error: Attribute "MyFunctionAttribute" cannot target class alias (allowed targets: function) in %s:%d -Stack trace: -#0 %s(%d): ReflectionAttribute->newInstance() -#1 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt deleted file mode 100644 index ce2abec6d3aec..0000000000000 --- a/Zend/tests/attributes/class_alias_target/not_repeatable-internal.phpt +++ /dev/null @@ -1,11 +0,0 @@ ---TEST-- -Validation of attribute repetition (not allowed; internal attribute) ---FILE-- - ---EXPECTF-- -Fatal error: Attribute "Deprecated" must not be repeated in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt deleted file mode 100644 index 5fdbbd8c687b4..0000000000000 --- a/Zend/tests/attributes/class_alias_target/not_repeatable-userland.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -Validation of attribute repetition (not allowed; userland attribute) ---FILE-- -getAttributes(); -var_dump($attributes); -$attributes[0]->newInstance(); - -?> ---EXPECTF-- -array(2) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } - [1]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } -} - -Fatal error: Uncaught Error: Attribute "MyAttribute" must not be repeated in %s:%d -Stack trace: -#0 %s(%d): ReflectionAttribute->newInstance() -#1 {main} - thrown in %s on line %d diff --git a/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt b/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt deleted file mode 100644 index fe8b1b805952a..0000000000000 --- a/Zend/tests/attributes/class_alias_target/repeatable-internal.phpt +++ /dev/null @@ -1,15 +0,0 @@ ---TEST-- -Validation of attribute repetition (is allowed; internal attribute) ---EXTENSIONS-- -zend_test ---FILE-- - ---EXPECT-- -Done diff --git a/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt b/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt deleted file mode 100644 index 305af9358e29e..0000000000000 --- a/Zend/tests/attributes/class_alias_target/repeatable-userland.phpt +++ /dev/null @@ -1,29 +0,0 @@ ---TEST-- -Validation of attribute repetition (is allowed; userland attribute) ---FILE-- -getAttributes(); -var_dump($attributes); -$attributes[0]->newInstance(); - -?> ---EXPECTF-- -array(2) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } - [1]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } -} diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt deleted file mode 100644 index 00c30aec2d5dd..0000000000000 --- a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-default.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -Attributes with TARGET_ALL (from the default) can target class aliases ---FILE-- -getAttributes(); -var_dump($attribs); -$attribs[0]->newInstance(); - -?> ---EXPECTF-- -array(1) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } -} diff --git a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt b/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt deleted file mode 100644 index 7b11a76aef861..0000000000000 --- a/Zend/tests/attributes/class_alias_target/target_all_targets_class_alias-explicit.phpt +++ /dev/null @@ -1,25 +0,0 @@ ---TEST-- -Attributes with TARGET_ALL (from an explicit parameter) can target class aliases ---FILE-- -getAttributes(); -var_dump($attribs); -$attribs[0]->newInstance(); - -?> ---EXPECTF-- -array(1) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(11) "MyAttribute" - } -} diff --git a/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt index dbc50b75f5566..8a979095e7b95 100644 --- a/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt +++ b/Zend/tests/attributes/constants/constant_listed_as_target-internal.phpt @@ -9,4 +9,4 @@ function demo( ?> --EXPECTF-- -Fatal error: Attribute "Deprecated" cannot target parameter (allowed targets: class, function, method, class constant, constant, class alias) in %s on line %d +Fatal error: Attribute "Deprecated" cannot target parameter (allowed targets: class, function, method, class constant, constant) in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt b/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt deleted file mode 100644 index 8b3885d964204..0000000000000 --- a/Zend/tests/attributes/deprecated/class_alias/access_constant.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -#[\Deprecated]: Class alias - access a constant ---FILE-- - ---EXPECTF-- -Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d -int(1) diff --git a/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt b/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt deleted file mode 100644 index 530c92125a9bf..0000000000000 --- a/Zend/tests/attributes/deprecated/class_alias/access_static_prop.phpt +++ /dev/null @@ -1,15 +0,0 @@ ---TEST-- -#[\Deprecated]: Class alias - access a static variable ---FILE-- - ---EXPECTF-- -Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt b/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt deleted file mode 100644 index bbaa89215bc8c..0000000000000 --- a/Zend/tests/attributes/deprecated/class_alias/call_static_method.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -#[\Deprecated]: Class alias - call a static method ---FILE-- - ---EXPECTF-- -Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d -Clazz::doNothing diff --git a/Zend/tests/attributes/deprecated/class_alias/messages.phpt b/Zend/tests/attributes/deprecated/class_alias/messages.phpt deleted file mode 100644 index 08ff0fdda74e7..0000000000000 --- a/Zend/tests/attributes/deprecated/class_alias/messages.phpt +++ /dev/null @@ -1,33 +0,0 @@ ---TEST-- -#[\Deprecated]: Class alias - all messages have details ---FILE-- - ---EXPECTF-- -Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d -int(1) - -Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d - -Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d - -Deprecated: Alias MyAlias for class Clazz is deprecated since 8.4, don't use the alias in %s on line %d diff --git a/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt b/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt deleted file mode 100644 index 1e09c3a0b7cfd..0000000000000 --- a/Zend/tests/attributes/deprecated/class_alias/use_constructor.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -#[\Deprecated]: Class alias - use the constructor ---FILE-- - ---EXPECTF-- -Deprecated: Alias MyAlias for class Clazz is deprecated in %s on line %d -Clazz::__construct diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 071e83aa52ace..a49bdcc2b8f1a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3617,9 +3617,6 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ */ zend_class_alias *alias = zend_class_alias_init(ce); - zend_string *original_name = zend_string_init(name, name_len, persistent); - alias->name = original_name; - ZVAL_ALIAS_PTR(&zv, alias); ret = zend_hash_add(CG(class_table), lcname, &zv); @@ -3632,7 +3629,6 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ return SUCCESS; } - zend_string_release(original_name); free(alias); return FAILURE; } diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 1dac53dc950c8..59773f9c8143e 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -23,7 +23,6 @@ #include "zend_attributes_arginfo.h" #include "zend_exceptions.h" #include "zend_smart_str.h" -#include "zend_class_alias.h" ZEND_API zend_class_entry *zend_ce_attribute; ZEND_API zend_class_entry *zend_ce_return_type_will_change_attribute; @@ -34,128 +33,11 @@ ZEND_API zend_class_entry *zend_ce_override; ZEND_API zend_class_entry *zend_ce_deprecated; ZEND_API zend_class_entry *zend_ce_nodiscard; ZEND_API zend_class_entry *zend_ce_delayed_target_validation; -ZEND_API zend_class_entry *zend_ce_class_alias; static zend_object_handlers attributes_object_handlers_sensitive_parameter_value; static HashTable internal_attributes; -// zend_compile.c not interface -zend_string *zend_resolve_class_name_ast(zend_ast *ast); - -// Based on zend_compile.c but not part of the interface -void compile_alias_attributes( - HashTable **attributes, zend_ast *ast -) /* {{{ */ { - zend_attribute *attr; - - zend_ast_list *list = zend_ast_get_list(ast); - - ZEND_ASSERT(ast->kind == ZEND_AST_ARRAY); - - for (uint32_t elem_idx = 0; elem_idx < list->children; elem_idx++) { - zend_ast *array_elem = list->child[elem_idx]; - ZEND_ASSERT(array_elem->kind == ZEND_AST_ARRAY_ELEM); - - zend_ast *array_content = array_elem->child[0]; - if (array_content->kind != ZEND_AST_NEW) { - zend_error_noreturn(E_COMPILE_ERROR, - "Attribute must be declared with `new`"); - } - - if (array_content->child[1] && - array_content->child[1]->kind == ZEND_AST_CALLABLE_CONVERT) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot create Closure as attribute argument"); - } - - zend_string *name = zend_resolve_class_name_ast(array_content->child[0]); - zend_ast_list *args = array_content->child[1] ? zend_ast_get_list(array_content->child[1]) : NULL; - - uint32_t flags = (CG(active_op_array)->fn_flags & ZEND_ACC_STRICT_TYPES) - ? ZEND_ATTRIBUTE_STRICT_TYPES : 0; - attr = zend_add_attribute( - attributes, name, args ? args->children : 0, flags, 0, array_content->lineno); - zend_string_release(name); - - /* Populate arguments */ - if (args) { - zend_attribute_populate_arguments(attr, args); - } - } - - if (*attributes != NULL) { - zend_apply_internal_attribute_validation(*attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); - } -} -/* }}} */ - -ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, uint32_t offset, uint32_t target) { - zend_attribute *attr; - zend_internal_attribute *config; - - /* Validate attributes in a secondary loop (needed to detect repeated attributes). */ - ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) { - if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) { - continue; - } - - if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) { - zend_string *location = zend_get_attribute_target_names(target); - zend_string *allowed = zend_get_attribute_target_names(config->flags); - - zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)", - ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed) - ); - } - - if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) { - if (zend_is_attribute_repeated(attributes, attr)) { - zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name)); - } - } - - if (config->validator != NULL) { - config->validator(attr, target, CG(active_class_entry)); - } - } ZEND_HASH_FOREACH_END(); -} - -ZEND_API void zend_attribute_populate_arguments(zend_attribute *attr, zend_ast_list *args) { - ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - - bool uses_named_args = 0; - for (uint32_t j = 0; j < args->children; j++) { - zend_ast **arg_ast_ptr = &args->child[j]; - zend_ast *arg_ast = *arg_ast_ptr; - - if (arg_ast->kind == ZEND_AST_UNPACK) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use unpacking in attribute argument list"); - } - - if (arg_ast->kind == ZEND_AST_NAMED_ARG) { - attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); - arg_ast_ptr = &arg_ast->child[1]; - uses_named_args = 1; - - for (uint32_t k = 0; k < j; k++) { - if (attr->args[k].name && - zend_string_equals(attr->args[k].name, attr->args[j].name)) { - zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", - ZSTR_VAL(attr->args[j].name)); - } - } - } else if (uses_named_args) { - zend_error_noreturn(E_COMPILE_ERROR, - "Cannot use positional argument after named argument"); - } - - zend_const_expr_to_zval( - &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); - } -} - uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. @@ -188,41 +70,6 @@ uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_ent return ZEND_ATTRIBUTE_TARGET_ALL; } -static zend_execute_data *setup_dummy_call_frame(zend_string *filename, zend_attribute *attribute_data) { - /* Set up dummy call frame that makes it look like the attribute was invoked - * from where it occurs in the code. */ - zend_function dummy_func; - zend_op *opline; - - memset(&dummy_func, 0, sizeof(zend_function)); - - zend_execute_data *call = zend_vm_stack_push_call_frame_ex( - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) + - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) + - ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)), - 0, &dummy_func, 0, NULL); - - opline = (zend_op*)(call + 1); - memset(opline, 0, sizeof(zend_op)); - opline->opcode = ZEND_DO_FCALL; - opline->lineno = attribute_data->lineno; - - call->opline = opline; - call->call = NULL; - call->return_value = NULL; - call->func = (zend_function*)(call->opline + 1); - call->prev_execute_data = EG(current_execute_data); - - memset(call->func, 0, sizeof(zend_function)); - call->func->type = ZEND_USER_FUNCTION; - call->func->op_array.fn_flags = - attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0; - call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE; - call->func->op_array.filename = filename; - - return call; -} - static void validate_allow_dynamic_properties( zend_attribute *attr, uint32_t target, zend_class_entry *scope) { @@ -282,158 +129,6 @@ static zend_string *validate_deprecated( } -static void validate_class_alias( - zend_attribute *attr, uint32_t target, zend_class_entry *scope) -{ - // Do NOT construct the attribute yet, that would require any of the - // attributes that are used to exist; at this point, access the alias name - // based on the arguments, and do our own validation - zend_execute_data *call = setup_dummy_call_frame(scope->info.user.filename, attr); - EG(current_execute_data) = call; - - zend_execute_data *constructor_call = zend_vm_stack_push_call_frame( - ZEND_CALL_TOP_FUNCTION | ZEND_CALL_DYNAMIC | ZEND_CALL_HAS_THIS, - zend_ce_class_alias->constructor, - attr->argc, - scope - ); - constructor_call->prev_execute_data = EG(current_execute_data); - EG(current_execute_data) = constructor_call; - - if (attr->argc < 1 || attr->argc > 2) { - zend_wrong_parameters_count_error(1, 2); - - goto restore_execution_data; - } - - zval *found = NULL; - - // Looking for either the first parameter, or if the first parameter - // is named, the 'alias' parameter - if (attr->args[0].name == NULL) { - found = &( attr->args[0].value ); - } else { - for (uint32_t i = 0; i < attr->argc; i++) { - if (zend_string_equals_literal( attr->args[i].name, "alias")) { - found = &( attr->args[i].value ); - break; - } - } - if (found == NULL) { - zend_argument_error(zend_ce_argument_count_error, 1, "not passed"); - goto restore_execution_data; - } - } - - zend_string *alias; - if (UNEXPECTED(!zend_parse_arg_str(found, &alias, false, 0))) { - zend_wrong_parameter_error( - ZPP_ERROR_WRONG_ARG, - 1, - "alias", - Z_EXPECTED_STRING, - found - ); - goto restore_execution_data; - } - - // if (CG(compiler_options) & ZEND_COMPILE_NO_CONSTANT_SUBSTITUTION) { - // // Opcache, don't register the alias - // goto restore_execution_data; - // } - - zend_result result = zend_register_class_alias_ex( - ZSTR_VAL(alias), - ZSTR_LEN(alias), - scope, - false - ); - if (result == FAILURE) { - zend_error_noreturn(E_ERROR, "Unable to declare alias '%s' for '%s'", - ZSTR_VAL(alias), - ZSTR_VAL(scope->name) - ); - goto restore_execution_data; - } - - zend_string *lc_name; - if (ZSTR_VAL(alias)[0] == '\\') { - lc_name = zend_string_alloc(ZSTR_LEN(alias) - 1, 0); - zend_str_tolower_copy(ZSTR_VAL(lc_name), ZSTR_VAL(alias) + 1, ZSTR_LEN(alias) - 1); - } else { - lc_name = zend_string_tolower(alias); - } - - zval *entry = zend_hash_find(EG(class_table), lc_name); - zend_string_release_ex(lc_name, /* persistent */ false); - ZEND_ASSERT(entry != NULL); - ZEND_ASSERT(Z_TYPE_P(entry) == IS_ALIAS_PTR); - - zend_class_alias *alias_obj = Z_CLASS_ALIAS_P(entry); - - // Compile attributes - if (attr->argc == 2) { - zval *nested_attribs = NULL; - if (attr->args[0].name == NULL && attr->args[1].name == NULL) { - nested_attribs = &( attr->args[1].value ); - } else { - for (uint32_t i = 0; i < attr->argc; i++) { - if ( attr->args[i].name == NULL ) { - continue; - } - if (zend_string_equals_literal( attr->args[i].name, "alias")) { - continue; - } - if (zend_string_equals_literal( attr->args[i].name, "attributes")) { - nested_attribs = &( attr->args[i].value ); - break; - } - zend_throw_error(NULL, "Unknown named parameter $%s", ZSTR_VAL(attr->args[i].name)); - goto restore_execution_data; - } - } - ZEND_ASSERT(nested_attribs != NULL); - if (UNEXPECTED(!Z_OPT_CONSTANT_P(nested_attribs))) { - // If it is an array, then it must be an array that can be evaluated - // already - if (Z_TYPE_P(nested_attribs) == IS_ARRAY) { - zend_argument_type_error( - 2, - "must be an array of objects" - ); - } else { - zend_wrong_parameter_error( - ZPP_ERROR_WRONG_ARG, - 2, - "attributes", - Z_EXPECTED_ARRAY, - nested_attribs - ); - // Something with an invalid parameter - } - } else { - zend_ast *attributes_ast = Z_ASTVAL_P(nested_attribs); - compile_alias_attributes(&( alias_obj->attributes), attributes_ast); - - zend_attribute *deprecated_attribute = zend_get_attribute_str( - alias_obj->attributes, - "deprecated", - strlen("deprecated") - ); - - if (deprecated_attribute) { - alias_obj->alias_flags |= ZEND_ACC_DEPRECATED; - } - } - } - -restore_execution_data: - EG(current_execute_data) = constructor_call->prev_execute_data; - zend_vm_stack_free_call_frame(constructor_call); - EG(current_execute_data) = call->prev_execute_data; - zend_vm_stack_free_call_frame(call); -} - ZEND_METHOD(Attribute, __construct) { zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL; @@ -570,42 +265,6 @@ ZEND_METHOD(NoDiscard, __construct) } } -ZEND_METHOD(ClassAlias, __construct) -{ - zend_string *alias = NULL; - HashTable *attributes = NULL; - - ZEND_PARSE_PARAMETERS_START(1, 2) - Z_PARAM_STR(alias) - Z_PARAM_OPTIONAL - Z_PARAM_ARRAY_HT(attributes) - ZEND_PARSE_PARAMETERS_END(); - - zval value; - ZVAL_STR(&value, alias); - zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("alias"), &value); - - /* The assignment might fail due to 'readonly'. */ - if (UNEXPECTED(EG(exception))) { - RETURN_THROWS(); - } - - if (attributes == NULL || zend_hash_num_elements(attributes) == 0) { - ZVAL_EMPTY_ARRAY(&value); - zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("attributes"), &value); - return; - } - - if (!zend_array_is_list(attributes)) { - zend_throw_error(NULL, - "ClassAlias::__construct(): Argument #2 ($attributes) must be a list, not an associative array" - ); - RETURN_THROWS(); - } - ZVAL_ARR(&value, attributes); - zend_update_property(zend_ce_class_alias, Z_OBJ_P(ZEND_THIS), ZEND_STRL("attributes"), &value); -} - static zend_attribute *get_attribute(const HashTable *attributes, const zend_string *lcname, uint32_t offset) { if (attributes) { @@ -679,7 +338,37 @@ ZEND_API zend_result zend_get_attribute_object(zval *obj, zend_class_entry *attr zend_execute_data *call = NULL; if (filename) { - call = setup_dummy_call_frame(filename, attribute_data); + /* Set up dummy call frame that makes it look like the attribute was invoked + * from where it occurs in the code. */ + zend_function dummy_func; + zend_op *opline; + + memset(&dummy_func, 0, sizeof(zend_function)); + + call = zend_vm_stack_push_call_frame_ex( + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_execute_data), sizeof(zval)) + + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_op), sizeof(zval)) + + ZEND_MM_ALIGNED_SIZE_EX(sizeof(zend_function), sizeof(zval)), + 0, &dummy_func, 0, NULL); + + opline = (zend_op*)(call + 1); + memset(opline, 0, sizeof(zend_op)); + opline->opcode = ZEND_DO_FCALL; + opline->lineno = attribute_data->lineno; + + call->opline = opline; + call->call = NULL; + call->return_value = NULL; + call->func = (zend_function*)(call->opline + 1); + call->prev_execute_data = EG(current_execute_data); + + memset(call->func, 0, sizeof(zend_function)); + call->func->type = ZEND_USER_FUNCTION; + call->func->op_array.fn_flags = + attribute_data->flags & ZEND_ATTRIBUTE_STRICT_TYPES ? ZEND_ACC_STRICT_TYPES : 0; + call->func->op_array.fn_flags |= ZEND_ACC_CALL_VIA_TRAMPOLINE; + call->func->op_array.filename = filename; + EG(current_execute_data) = call; } @@ -738,8 +427,7 @@ static const char *target_names[] = { "property", "class constant", "parameter", - "constant", - "class alias" + "constant" }; ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags) @@ -918,10 +606,6 @@ void zend_register_attribute_ce(void) zend_ce_delayed_target_validation = register_class_DelayedTargetValidation(); attr = zend_mark_internal_attribute(zend_ce_delayed_target_validation); - - zend_ce_class_alias = register_class_ClassAlias(); - attr = zend_mark_internal_attribute(zend_ce_class_alias); - attr->validator = validate_class_alias; } void zend_attributes_shutdown(void) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 12a4ed24ad32c..f8b61ac9d1666 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -30,10 +30,9 @@ #define ZEND_ATTRIBUTE_TARGET_CLASS_CONST (1<<4) #define ZEND_ATTRIBUTE_TARGET_PARAMETER (1<<5) #define ZEND_ATTRIBUTE_TARGET_CONST (1<<6) -#define ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS (1<<7) -#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<8) - 1) -#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<8) -#define ZEND_ATTRIBUTE_FLAGS ((1<<9) - 1) +#define ZEND_ATTRIBUTE_TARGET_ALL ((1<<7) - 1) +#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<7) +#define ZEND_ATTRIBUTE_FLAGS ((1<<8) - 1) /* Flags for zend_attribute.flags */ #define ZEND_ATTRIBUTE_PERSISTENT (1<<0) @@ -100,9 +99,6 @@ ZEND_API zend_attribute *zend_add_attribute( uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope); -ZEND_API void zend_apply_internal_attribute_validation(HashTable *attributes, uint32_t offset, uint32_t target); -ZEND_API void zend_attribute_populate_arguments(zend_attribute *attr, zend_ast_list *args); - END_EXTERN_C() static zend_always_inline zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc) diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index 8ad621311d115..fa59f4df50c13 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -19,8 +19,6 @@ final class Attribute const int TARGET_PARAMETER = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_TARGET_CONST */ const int TARGET_CONSTANT = UNKNOWN; - /** @cvalue ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS */ - const int TARGET_CLASS_ALIAS = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_TARGET_ALL */ const int TARGET_ALL = UNKNOWN; /** @cvalue ZEND_ATTRIBUTE_IS_REPEATABLE */ @@ -79,7 +77,7 @@ public function __construct() {} /** * @strict-properties */ -#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT|Attribute::TARGET_CLASS|Attribute::TARGET_CLASS_ALIAS)] +#[Attribute(Attribute::TARGET_METHOD|Attribute::TARGET_FUNCTION|Attribute::TARGET_CLASS_CONSTANT|Attribute::TARGET_CONSTANT|Attribute::TARGET_CLASS)] final class Deprecated { public readonly ?string $message; @@ -106,15 +104,3 @@ public function __construct(?string $message = null) {} #[Attribute(Attribute::TARGET_ALL)] final class DelayedTargetValidation {} -/** - * @strict-properties - */ -#[Attribute(Attribute::TARGET_CLASS|Attribute::IS_REPEATABLE)] -final class ClassAlias -{ - public readonly string $alias; - - public readonly array $attributes; - - public function __construct(string $alias, array $attributes = []) {} -} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index e31c72544f4b1..0707290d262e1 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: 9f03d6d181126bf0d2116708ad1cdc2476ab46fd */ + * Stub hash: e0dc3e439ff4db8f60fbd7d550537fa4712bfd0f */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") @@ -33,11 +33,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_NoDiscard___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, message, IS_STRING, 1, "null") ZEND_END_ARG_INFO() -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ClassAlias___construct, 0, 0, 1) - ZEND_ARG_TYPE_INFO(0, alias, IS_STRING, 0) - ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, attributes, IS_ARRAY, 0, "[]") -ZEND_END_ARG_INFO() - ZEND_METHOD(Attribute, __construct); ZEND_METHOD(ReturnTypeWillChange, __construct); ZEND_METHOD(AllowDynamicProperties, __construct); @@ -48,7 +43,6 @@ ZEND_METHOD(SensitiveParameterValue, __debugInfo); ZEND_METHOD(Override, __construct); ZEND_METHOD(Deprecated, __construct); ZEND_METHOD(NoDiscard, __construct); -ZEND_METHOD(ClassAlias, __construct); static const zend_function_entry class_Attribute_methods[] = { ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC) @@ -92,11 +86,6 @@ static const zend_function_entry class_NoDiscard_methods[] = { ZEND_FE_END }; -static const zend_function_entry class_ClassAlias_methods[] = { - ZEND_ME(ClassAlias, __construct, arginfo_class_ClassAlias___construct, ZEND_ACC_PUBLIC) - ZEND_FE_END -}; - static zend_class_entry *register_class_Attribute(void) { zend_class_entry ce, *class_entry; @@ -146,12 +135,6 @@ static zend_class_entry *register_class_Attribute(void) zend_declare_typed_class_constant(class_entry, const_TARGET_CONSTANT_name, &const_TARGET_CONSTANT_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release_ex(const_TARGET_CONSTANT_name, true); - zval const_TARGET_CLASS_ALIAS_value; - ZVAL_LONG(&const_TARGET_CLASS_ALIAS_value, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); - zend_string *const_TARGET_CLASS_ALIAS_name = zend_string_init_interned("TARGET_CLASS_ALIAS", sizeof("TARGET_CLASS_ALIAS") - 1, true); - zend_declare_typed_class_constant(class_entry, const_TARGET_CLASS_ALIAS_name, &const_TARGET_CLASS_ALIAS_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); - zend_string_release_ex(const_TARGET_CLASS_ALIAS_name, true); - zval const_TARGET_ALL_value; ZVAL_LONG(&const_TARGET_ALL_value, ZEND_ATTRIBUTE_TARGET_ALL); zend_string *const_TARGET_ALL_name = zend_string_init_interned("TARGET_ALL", sizeof("TARGET_ALL") - 1, true); @@ -270,7 +253,7 @@ static zend_class_entry *register_class_Deprecated(void) zend_string *attribute_name_Attribute_class_Deprecated_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); zend_attribute *attribute_Attribute_class_Deprecated_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Deprecated_0, 1); zend_string_release_ex(attribute_name_Attribute_class_Deprecated_0, true); - ZVAL_LONG(&attribute_Attribute_class_Deprecated_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST | ZEND_ATTRIBUTE_TARGET_CLASS | ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS); + ZVAL_LONG(&attribute_Attribute_class_Deprecated_0->args[0].value, ZEND_ATTRIBUTE_TARGET_METHOD | ZEND_ATTRIBUTE_TARGET_FUNCTION | ZEND_ATTRIBUTE_TARGET_CLASS_CONST | ZEND_ATTRIBUTE_TARGET_CONST | ZEND_ATTRIBUTE_TARGET_CLASS); return class_entry; } @@ -308,30 +291,3 @@ static zend_class_entry *register_class_DelayedTargetValidation(void) return class_entry; } - -static zend_class_entry *register_class_ClassAlias(void) -{ - zend_class_entry ce, *class_entry; - - INIT_CLASS_ENTRY(ce, "ClassAlias", class_ClassAlias_methods); - class_entry = zend_register_internal_class_with_flags(&ce, NULL, ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES); - - zval property_alias_default_value; - ZVAL_UNDEF(&property_alias_default_value); - zend_string *property_alias_name = zend_string_init("alias", sizeof("alias") - 1, true); - zend_declare_typed_property(class_entry, property_alias_name, &property_alias_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release_ex(property_alias_name, true); - - zval property_attributes_default_value; - ZVAL_UNDEF(&property_attributes_default_value); - zend_string *property_attributes_name = zend_string_init("attributes", sizeof("attributes") - 1, true); - zend_declare_typed_property(class_entry, property_attributes_name, &property_attributes_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_ARRAY)); - zend_string_release_ex(property_attributes_name, true); - - zend_string *attribute_name_Attribute_class_ClassAlias_0 = zend_string_init_interned("Attribute", sizeof("Attribute") - 1, true); - zend_attribute *attribute_Attribute_class_ClassAlias_0 = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ClassAlias_0, 1); - zend_string_release_ex(attribute_name_Attribute_class_ClassAlias_0, true); - ZVAL_LONG(&attribute_Attribute_class_ClassAlias_0->args[0].value, ZEND_ATTRIBUTE_TARGET_CLASS | ZEND_ATTRIBUTE_IS_REPEATABLE); - - return class_entry; -} diff --git a/Zend/zend_class_alias.c b/Zend/zend_class_alias.c index 7a2db3fe47b11..ddadcbce78582 100644 --- a/Zend/zend_class_alias.c +++ b/Zend/zend_class_alias.c @@ -17,8 +17,6 @@ */ #include "zend_class_alias.h" -#include "zend_errors.h" -#include "zend_string.h" #include "zend.h" zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { @@ -27,27 +25,7 @@ zend_class_alias * zend_class_alias_init(zend_class_entry *ce) { GC_SET_REFCOUNT(alias, 1); alias->ce = ce; - alias->name = NULL; - alias->attributes = NULL; alias->alias_flags = 0; return alias; } - -ZEND_COLD zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix); - -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_alias(const zend_class_alias *alias) { - zend_string *message_suffix = ZSTR_EMPTY_ALLOC(); - - if (get_deprecation_suffix_from_attribute(alias->attributes, NULL, &message_suffix) == FAILURE) { - return; - } - - zend_error_unchecked(E_USER_DEPRECATED, "Alias %s for class %s is deprecated%S", - ZSTR_VAL(alias->name), - ZSTR_VAL(alias->ce->name), - message_suffix - ); - - zend_string_release(message_suffix); -} diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h index 6ca62eb2fa940..e087332713ce0 100644 --- a/Zend/zend_class_alias.h +++ b/Zend/zend_class_alias.h @@ -24,8 +24,6 @@ struct _zend_class_alias { zend_refcounted_h gc; zend_class_entry *ce; - zend_string *name; - HashTable *attributes; uint32_t alias_flags; }; @@ -52,6 +50,5 @@ typedef struct _zend_class_alias zend_class_alias; zend_class_alias * zend_class_alias_init(zend_class_entry *ce); -ZEND_API ZEND_COLD void ZEND_FASTCALL zend_deprecated_class_alias(const zend_class_alias *alias); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 65559602b5f1e..c672113a3915d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1223,7 +1223,7 @@ static zend_string *zend_resolve_class_name(zend_string *name, uint32_t type) /* } /* }}} */ -zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ +static zend_string *zend_resolve_class_name_ast(zend_ast *ast) /* {{{ */ { const zval *class_name = zend_ast_get_zval(ast); if (Z_TYPE_P(class_name) != IS_STRING) { @@ -1892,17 +1892,7 @@ static bool zend_try_ct_eval_class_const(zval *zv, zend_string *class_name, zend if (ce_or_alias) { zend_class_entry *ce; - if (Z_TYPE_P(ce_or_alias) == IS_ALIAS_PTR) { - zend_class_alias *alias = Z_CLASS_ALIAS_P(ce_or_alias); - if (alias->alias_flags & ZEND_ACC_DEPRECATED) { - // Cannot evaluate at compile time - return 0; - } - ce = alias->ce; - } else { - ZEND_ASSERT(Z_TYPE_P(ce_or_alias) == IS_PTR); - ce = Z_PTR_P(ce_or_alias); - } + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); cc = zend_hash_find_ptr(&ce->constants_table, name); } else { return 0; @@ -7852,7 +7842,38 @@ static void zend_compile_attributes( /* Populate arguments */ if (args) { - zend_attribute_populate_arguments(attr, args); + ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); + + bool uses_named_args = 0; + for (j = 0; j < args->children; j++) { + zend_ast **arg_ast_ptr = &args->child[j]; + zend_ast *arg_ast = *arg_ast_ptr; + + if (arg_ast->kind == ZEND_AST_UNPACK) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use unpacking in attribute argument list"); + } + + if (arg_ast->kind == ZEND_AST_NAMED_ARG) { + attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); + arg_ast_ptr = &arg_ast->child[1]; + uses_named_args = 1; + + for (uint32_t k = 0; k < j; k++) { + if (attr->args[k].name && + zend_string_equals(attr->args[k].name, attr->args[j].name)) { + zend_error_noreturn(E_COMPILE_ERROR, "Duplicate named parameter $%s", + ZSTR_VAL(attr->args[j].name)); + } + } + } else if (uses_named_args) { + zend_error_noreturn(E_COMPILE_ERROR, + "Cannot use positional argument after named argument"); + } + + zend_const_expr_to_zval( + &attr->args[j].value, arg_ast_ptr, /* allow_dynamic */ true); + } } } } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 4ee4b503af2de..37278c5cb9a23 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -1798,7 +1798,7 @@ ZEND_API ZEND_COLD void zend_wrong_string_offset_error(void) zend_throw_error(NULL, "%s", msg); } -ZEND_COLD zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) +ZEND_COLD static zend_result ZEND_FASTCALL get_deprecation_suffix_from_attribute(HashTable *attributes, zend_class_entry* scope, zend_string **message_suffix) { *message_suffix = ZSTR_EMPTY_ALLOC(); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 396149d3b341a..31aabd7e5cbef 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1205,11 +1205,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_string_release_ex(lc_name, 0); } Z_CE_FROM_ZVAL_P(ce, zv); - zend_class_alias *alias = NULL; - if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { - ce_cache = 0; - alias = Z_CLASS_ALIAS_P(zv); - } if (UNEXPECTED(!(ce->ce_flags & ZEND_ACC_LINKED))) { if ((flags & ZEND_FETCH_CLASS_ALLOW_UNLINKED) || ((flags & ZEND_FETCH_CLASS_ALLOW_NEARLY_LINKED) && @@ -1219,9 +1214,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_hash_init(CG(unlinked_uses), 0, NULL, NULL, 0); } zend_hash_index_add_empty_element(CG(unlinked_uses), (zend_long)(uintptr_t)ce); - if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { - zend_deprecated_class_alias(alias); - } return ce; } return NULL; @@ -1232,9 +1224,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * (!CG(in_compilation) || (ce->ce_flags & ZEND_ACC_IMMUTABLE))) { SET_CE_CACHE(ce_cache, ce); } - if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { - zend_deprecated_class_alias(alias); - } return ce; } @@ -1305,9 +1294,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * SET_CE_CACHE(ce_cache, ce); } } - if (!(flags & ZEND_FETCH_CLASS_SILENT) && alias && (alias->alias_flags & ZEND_ACC_DEPRECATED)) { - zend_deprecated_class_alias(alias); - } return ce; } /* }}} */ diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0b5e13b2295b1..adfb4c0dbdf3e 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -315,14 +315,6 @@ ZEND_API void destroy_zend_class(zval *zv) return; } - if (class_alias->attributes) { - zend_hash_release(class_alias->attributes); - // class_alias->attributes = NULL; - } - if (class_alias->name) { - zend_string_release(class_alias->name); - // class_alias->name = NULL; - } free(class_alias); return; } diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index d8d090a71de99..c89c4c3648ff2 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -687,15 +687,7 @@ static void accel_copy_permanent_strings(zend_new_interned_string_func_t new_int /* class table hash keys, class names, properties, methods, constants, etc */ ZEND_HASH_MAP_FOREACH_BUCKET(CG(class_table), p) { zend_class_entry *ce = NULL; - - if (EXPECTED(Z_TYPE(p->val) == IS_PTR)) { - ce = Z_PTR(p->val); - } else { - ZEND_ASSERT(Z_TYPE(p->val) == IS_ALIAS_PTR); - zend_class_alias *alias = Z_PTR(p->val); - alias->name = new_interned_string(alias->name); - ce = alias->ce; - } + Z_CE_FROM_ZVAL(ce, p->val); if (p->key) { p->key = new_interned_string(p->key); @@ -4188,14 +4180,6 @@ static void preload_link(void) continue; } - // Not preloading class constants for deprecated aliases - if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { - zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); - if (class_alias->alias_flags & ZEND_ACC_DEPRECATED) { - continue; - } - } - if ((ce->ce_flags & ZEND_ACC_LINKED) && !(ce->ce_flags & ZEND_ACC_CONSTANTS_UPDATED)) { if (!(ce->ce_flags & ZEND_ACC_TRAIT)) { /* don't update traits */ CG(in_compilation) = true; /* prevent autoloading */ diff --git a/ext/opcache/jit/zend_jit.c b/ext/opcache/jit/zend_jit.c index c30aa9c48e84c..3ffb669e84742 100644 --- a/ext/opcache/jit/zend_jit.c +++ b/ext/opcache/jit/zend_jit.c @@ -575,9 +575,7 @@ static zend_class_entry* zend_get_known_class(const zend_op_array *op_array, con ZEND_ASSERT(Z_TYPE_P(zv) == IS_STRING); class_name = Z_STR_P(zv); - // ZEND_FETCH_CLASS_SILENT - ignore alias deprecation - // TODO but we want to not cache the ce if i is a deprecated alias - ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD | ZEND_FETCH_CLASS_SILENT); + ce = zend_lookup_class_ex(class_name, NULL, ZEND_FETCH_CLASS_NO_AUTOLOAD); if (ce && (ce->type == ZEND_INTERNAL_CLASS || ce->info.user.filename != op_array->filename)) { ce = NULL; } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index c0fca27171106..5a3e8428c4f51 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -920,10 +920,6 @@ zend_class_alias *zend_persist_class_alias_entry(zend_class_alias *orig_alias) alias = zend_shared_memdup_put(alias, sizeof(zend_class_alias)); alias->ce = zend_persist_class_entry(alias->ce); - // zend_accel_store_string(alias->name); - if (alias->attributes) { - alias->attributes = zend_persist_attributes(alias->attributes); - } if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { alias->alias_flags |= ZEND_ACC_IMMUTABLE; diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 271fc00118604..98f7ad3324e12 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -602,11 +602,6 @@ static void zend_persist_class_alias_entry_calc(const zend_class_alias *alias) // alias->ce is going to be a pointer to a class entry that will be // persisted on its own, here we just need to add size for the alias ADD_SIZE(sizeof(zend_class_alias)); - // And the things that the alias holds directly - ADD_INTERNED_STRING(alias->name); - if (alias->attributes) { - zend_persist_attributes_calc(alias->attributes); - } zend_persist_class_entry_calc(alias->ce); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 0f47bccc51bac..fe47e34f5a2dd 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -8203,18 +8203,6 @@ ZEND_METHOD(ReflectionClassAlias, __construct) ZVAL_STR_COPY(name_zv, name); } -ZEND_METHOD(ReflectionClassAlias, getAttributes) -{ - reflection_object *intern; - zend_class_alias *alias; - - GET_REFLECTION_OBJECT_PTR(alias); - - reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, - alias->attributes, 0, alias->ce, ZEND_ATTRIBUTE_TARGET_CLASS_ALIAS, - NULL); -} - ZEND_METHOD(ReflectionClassAlias, __toString) { reflection_object *intern; @@ -8227,25 +8215,13 @@ ZEND_METHOD(ReflectionClassAlias, __toString) smart_str_append_printf( &str, - "%s - %salias for %s", + "%s - alias for %s", Z_STRVAL_P(reflection_prop_name(ZEND_THIS)), - (alias->alias_flags & ZEND_ACC_DEPRECATED) ? "deprecated " : "", ZSTR_VAL(alias->ce->name) ); RETURN_STR(smart_str_extract(&str)); } -ZEND_METHOD(ReflectionClassAlias, isDeprecated) -{ - reflection_object *intern; - zend_class_alias *alias; - - ZEND_PARSE_PARAMETERS_NONE(); - - GET_REFLECTION_OBJECT_PTR(alias); - RETURN_BOOL(alias->alias_flags & ZEND_ACC_DEPRECATED); -} - PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 00b9727d9e067..492eb210113b3 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -951,8 +951,4 @@ final class ReflectionClassAlias implements Reflector public function __construct(string $name) {} public function __toString(): string {} - - public function isDeprecated(): bool {} - - public function getAttributes(?string $name = null, int $flags = 0): array {} } diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 76aa6d93d843c..fd49d58879fc3 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 360dcefc11527b30c94cc59f8475a25a8a0a90ca + * Stub hash: 0c4bbfe6c6f50fbd6dcc994a25fb248bd2928ef3 * Has decl header: yes */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) @@ -737,10 +737,6 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassAlias___toString arginfo_class_ReflectionFunction___toString -#define arginfo_class_ReflectionClassAlias_isDeprecated arginfo_class_ReflectionFunctionAbstract_hasTentativeReturnType - -#define arginfo_class_ReflectionClassAlias_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes - ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); ZEND_METHOD(ReflectionFunctionAbstract, inNamespace); @@ -1016,8 +1012,6 @@ ZEND_METHOD(ReflectionConstant, __toString); ZEND_METHOD(ReflectionConstant, getAttributes); ZEND_METHOD(ReflectionClassAlias, __construct); ZEND_METHOD(ReflectionClassAlias, __toString); -ZEND_METHOD(ReflectionClassAlias, isDeprecated); -ZEND_METHOD(ReflectionClassAlias, getAttributes); static const zend_function_entry class_Reflection_methods[] = { ZEND_ME(Reflection, getModifierNames, arginfo_class_Reflection_getModifierNames, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -1397,8 +1391,6 @@ static const zend_function_entry class_ReflectionConstant_methods[] = { static const zend_function_entry class_ReflectionClassAlias_methods[] = { ZEND_ME(ReflectionClassAlias, __construct, arginfo_class_ReflectionClassAlias___construct, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassAlias, __toString, arginfo_class_ReflectionClassAlias___toString, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionClassAlias, isDeprecated, arginfo_class_ReflectionClassAlias_isDeprecated, ZEND_ACC_PUBLIC) - ZEND_ME(ReflectionClassAlias, getAttributes, arginfo_class_ReflectionClassAlias_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; diff --git a/ext/reflection/php_reflection_decl.h b/ext/reflection/php_reflection_decl.h index bb7594cc7ad1d..312e5a80a844f 100644 --- a/ext/reflection/php_reflection_decl.h +++ b/ext/reflection/php_reflection_decl.h @@ -1,12 +1,12 @@ /* This is a generated file, edit php_reflection.stub.php instead. - * Stub hash: 360dcefc11527b30c94cc59f8475a25a8a0a90ca */ + * Stub hash: 0c4bbfe6c6f50fbd6dcc994a25fb248bd2928ef3 */ -#ifndef ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H -#define ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H +#ifndef ZEND_PHP_REFLECTION_DECL_0c4bbfe6c6f50fbd6dcc994a25fb248bd2928ef3_H +#define ZEND_PHP_REFLECTION_DECL_0c4bbfe6c6f50fbd6dcc994a25fb248bd2928ef3_H typedef enum zend_enum_PropertyHookType { ZEND_ENUM_PropertyHookType_Get = 1, ZEND_ENUM_PropertyHookType_Set = 2, } zend_enum_PropertyHookType; -#endif /* ZEND_PHP_REFLECTION_DECL_360dcefc11527b30c94cc59f8475a25a8a0a90ca_H */ +#endif /* ZEND_PHP_REFLECTION_DECL_0c4bbfe6c6f50fbd6dcc994a25fb248bd2928ef3_H */ diff --git a/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt b/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt deleted file mode 100644 index 746c847abdc7b..0000000000000 --- a/ext/reflection/tests/ReflectionClassAlias_getAttributes_empty.phpt +++ /dev/null @@ -1,14 +0,0 @@ ---TEST-- -ReflectionClassAlias::__toString() ---FILE-- -getAttributes() ); - -?> ---EXPECT-- -array(0) { -} diff --git a/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt b/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt deleted file mode 100644 index 1dcdcb1402cd0..0000000000000 --- a/ext/reflection/tests/ReflectionClassAlias_getAttributes_non-empty.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -ReflectionClassAlias::__toString() ---FILE-- -getAttributes() ); - -?> ---EXPECTF-- -array(1) { - [0]=> - object(ReflectionAttribute)#%d (1) { - ["name"]=> - string(16) "MissingAttribute" - } -} diff --git a/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt b/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt deleted file mode 100644 index 4c27321782692..0000000000000 --- a/ext/reflection/tests/ReflectionClassAlias_isDeprecated.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -ReflectionClassAlias::isDeprecated() ---FILE-- -isDeprecated() ); - -$r = new ReflectionClassAlias( 'NewAlias' ); -var_dump( $r->isDeprecated() ); -?> ---EXPECT-- -bool(true) -bool(false) diff --git a/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt b/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt deleted file mode 100644 index 656cf874bb24e..0000000000000 --- a/ext/reflection/tests/ReflectionClassAlias_toString_deprecated.phpt +++ /dev/null @@ -1,13 +0,0 @@ ---TEST-- -ReflectionClassAlias::__toString() for a deprecated alias ---FILE-- - ---EXPECT-- -Other - deprecated alias for Demo From 545bb6d9120a95c1b6573f9b243c51a4f87c17c3 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 16 Jun 2025 01:35:53 -0700 Subject: [PATCH 23/30] Z_TYPE_P --- Zend/zend_opcode.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index adfb4c0dbdf3e..6f491fb926b4b 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -308,7 +308,7 @@ ZEND_API void destroy_zend_class(zval *zv) * so we don't need to destroy the underlying ->ce here, but we do need * to free the attributes and the storage for the * skip the destruction of aliases entirely. */ - if (UNEXPECTED(Z_TYPE_INFO_P(zv) == IS_ALIAS_PTR)) { + if (UNEXPECTED(Z_TYPE_P(zv) == IS_ALIAS_PTR)) { zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); if (class_alias->alias_flags & ZEND_ACC_IMMUTABLE) { From 4d74ef163e66150f77f1eebac511b439a8967cfe Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 16 Jun 2025 01:59:56 -0700 Subject: [PATCH 24/30] Cache --- Zend/zend_execute_API.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 31aabd7e5cbef..58b5447afac5c 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1270,7 +1270,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_class_alias *alias = NULL; if (ce_zval) { if (Z_TYPE_P(ce_zval) == IS_ALIAS_PTR) { - ce_cache = 0; alias = Z_CLASS_ALIAS_P(ce_zval); ce = alias->ce; } else { From 0c4fd1267fecc55cf660a0bd96f1719e65f599f1 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 17:06:06 -0800 Subject: [PATCH 25/30] Rebase issues --- Zend/zend_attributes.c | 4 ++-- Zend/zend_attributes.stub.php | 1 - Zend/zend_compile.c | 4 ++-- ext/opcache/zend_accelerator_util_funcs.c | 6 +++--- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 59773f9c8143e..cba95810ba496 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -38,7 +38,7 @@ static zend_object_handlers attributes_object_handlers_sensitive_parameter_value static HashTable internal_attributes; -uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_entry *scope) +uint32_t zend_attribute_attribute_get_flags(const zend_attribute *attr, zend_class_entry *scope) { // TODO: More proper signature validation: Too many args, incorrect arg names. if (attr->argc > 0) { @@ -70,7 +70,7 @@ uint32_t zend_attribute_attribute_get_flags(zend_attribute *attr, zend_class_ent return ZEND_ATTRIBUTE_TARGET_ALL; } -static void validate_allow_dynamic_properties( +static zend_string *validate_allow_dynamic_properties( zend_attribute *attr, uint32_t target, zend_class_entry *scope) { ZEND_ASSERT(scope != NULL); diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index fa59f4df50c13..ded9c89593a36 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -103,4 +103,3 @@ public function __construct(?string $message = null) {} */ #[Attribute(Attribute::TARGET_ALL)] final class DelayedTargetValidation {} - diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c672113a3915d..76e03e31293d9 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -7844,7 +7844,7 @@ static void zend_compile_attributes( if (args) { ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - bool uses_named_args = 0; + bool uses_named_args = false; for (j = 0; j < args->children; j++) { zend_ast **arg_ast_ptr = &args->child[j]; zend_ast *arg_ast = *arg_ast_ptr; @@ -7857,7 +7857,7 @@ static void zend_compile_attributes( if (arg_ast->kind == ZEND_AST_NAMED_ARG) { attr->args[j].name = zend_string_copy(zend_ast_get_str(arg_ast->child[0])); arg_ast_ptr = &arg_ast->child[1]; - uses_named_args = 1; + uses_named_args = true; for (uint32_t k = 0; k < j; k++) { if (attr->args[k].name && diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 6546f1543a139..7cd2f3b99c4e6 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -214,7 +214,7 @@ static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, co * value. */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { - zend_class_entry *ce1; + const zend_class_entry *ce1; Z_CE_FROM_ZVAL(ce1, p->val); if (!(ce1->ce_flags & ZEND_ACC_ANON_CLASS)) { CG(in_compilation) = 1; @@ -354,8 +354,8 @@ static void zend_accel_do_delayed_early_binding( CG(compiled_filename) = persistent_script->script.filename; CG(in_compilation) = 1; for (uint32_t i = 0; i < persistent_script->num_early_bindings; i++) { - zend_early_binding *early_binding = &persistent_script->early_bindings[i]; - zval *ce_or_alias = zend_hash_find_ex(EG(class_table), early_binding->lcname, 1); + const zend_early_binding *early_binding = &persistent_script->early_bindings[i]; + const zval *ce_or_alias = zend_hash_find_ex(EG(class_table), early_binding->lcname, 1); if (!ce_or_alias) { zval *zv = zend_hash_find_known_hash(EG(class_table), early_binding->rtd_key); zend_class_entry *ce = NULL; From 1a4cbd048de22bbd6119101f15eac43789a37ab5 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 17:16:17 -0800 Subject: [PATCH 26/30] Arginfo --- Zend/zend_attributes_arginfo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 0707290d262e1..54a66af29966d 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit zend_attributes.stub.php instead. - * Stub hash: e0dc3e439ff4db8f60fbd7d550537fa4712bfd0f */ + * Stub hash: b868cb33f41d9442f42d0cec84e33fcc09f5d88c */ ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL") From 8816f09349e2616689bac68867949205404edfd0 Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 17:22:52 -0800 Subject: [PATCH 27/30] const --- ext/opcache/zend_accelerator_util_funcs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 7cd2f3b99c4e6..b1fea5675d2dc 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -191,7 +191,7 @@ static zend_never_inline void zend_accel_function_hash_copy_notify(HashTable *ta static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, const HashTable *source, bool call_observers) { - const Bucket *p, *end; + Bucket *p, *end; const zval *t; zend_hash_extend(target, target->nNumUsed + source->nNumUsed, 0); From ab8bf664525d957182a455ddeb829d400a0d082e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 17:48:25 -0800 Subject: [PATCH 28/30] accel_reset_arena_info --- ext/opcache/ZendAccelerator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ext/opcache/ZendAccelerator.c b/ext/opcache/ZendAccelerator.c index c89c4c3648ff2..3bd250fde9316 100644 --- a/ext/opcache/ZendAccelerator.c +++ b/ext/opcache/ZendAccelerator.c @@ -4635,7 +4635,9 @@ static void accel_reset_arena_info(zend_persistent_script *script) ZEND_HASH_MAP_FOREACH_PTR(&script->script.function_table, op_array) { zend_accel_clear_call_graph_ptrs(op_array); } ZEND_HASH_FOREACH_END(); - ZEND_HASH_MAP_FOREACH_PTR(&script->script.class_table, ce) { + zval *ce_or_alias; + ZEND_HASH_MAP_FOREACH_VAL(&script->script.class_table, ce_or_alias) { + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); ZEND_HASH_MAP_FOREACH_PTR(&ce->function_table, op_array) { if (op_array->scope == ce && op_array->type == ZEND_USER_FUNCTION From faf3cc06d8eebc2bb4e0774de6da0052a844290a Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Mon, 1 Dec 2025 17:51:15 -0800 Subject: [PATCH 29/30] xfail --- ext/opcache/tests/gh8846.phpt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/tests/gh8846.phpt b/ext/opcache/tests/gh8846.phpt index 1e9dd68f191cf..6549fb62306d9 100644 --- a/ext/opcache/tests/gh8846.phpt +++ b/ext/opcache/tests/gh8846.phpt @@ -7,6 +7,8 @@ server --INI-- opcache.validate_timestamps=1 opcache.revalidate_freq=0 +--XFAIL-- +TODO --FILE-- Date: Thu, 19 Mar 2026 20:54:19 -0700 Subject: [PATCH 30/30] Rebase fixes --- Zend/zend_autoload.c | 21 +++++++++++++-------- Zend/zend_autoload.h | 2 +- Zend/zend_execute_API.c | 2 -- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/Zend/zend_autoload.c b/Zend/zend_autoload.c index bc74efa1afdaa..38e6353f25307 100644 --- a/Zend/zend_autoload.c +++ b/Zend/zend_autoload.c @@ -17,6 +17,7 @@ */ #include "zend.h" +#include "zend_class_alias.h" #include "zend_API.h" #include "zend_autoload.h" #include "zend_hash.h" @@ -44,7 +45,7 @@ static Bucket *autoload_find_registered_function(const HashTable *autoloader_tab return NULL; } -ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name) +ZEND_API zval *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name) { if (!zend_class_autoload_functions) { return NULL; @@ -69,13 +70,17 @@ ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, if (EG(exception)) { return NULL; } - if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { - return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); - } - - zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); - if (ce) { - return ce; + // if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { + // return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); + // } + + // zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); + zval *ce_zv = zend_hash_find(EG(class_table), lc_name); + if (ce_zv) { + return ce_zv; + // zend_class_entry *ce; + // Z_CE_FROM_ZVAL_P(ce, ce_zv); + // return ce; } zend_hash_move_forward_ex(class_autoload_functions, &pos); diff --git a/Zend/zend_autoload.h b/Zend/zend_autoload.h index fde4a4a82e9ad..dd21704045320 100644 --- a/Zend/zend_autoload.h +++ b/Zend/zend_autoload.h @@ -24,7 +24,7 @@ #include "zend_API.h" #include "zend.h" -ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name); +ZEND_API zval *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name); ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend); ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc); ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 58b5447afac5c..5e4a78403d267 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -1265,7 +1265,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * zend_long previous_lineno = EG(lineno_override); EG(filename_override) = NULL; EG(lineno_override) = -1; - zend_exception_save(); zval *ce_zval = zend_autoload(autoload_name, lc_name); zend_class_alias *alias = NULL; if (ce_zval) { @@ -1277,7 +1276,6 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * ce = Z_PTR_P(ce_zval); } } - zend_exception_restore(); EG(filename_override) = previous_filename; EG(lineno_override) = previous_lineno;