diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index b57ad3ad4268e..6f2c02a82d94f 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) { const Bucket *ce_bucket = (const Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); @@ -812,14 +814,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)) { @@ -862,7 +872,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/zend_API.c b/Zend/zend_API.c index 601d753bd2aa6..f377bceb8689a 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -34,6 +34,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; @@ -3276,8 +3280,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); } @@ -3590,7 +3595,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); @@ -3601,6 +3608,8 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ } return SUCCESS; } + + free(alias); return FAILURE; } /* }}} */ diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index a4ece3061d5d0..2a3b19675151e 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" @@ -1431,7 +1432,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..ddadcbce78582 --- /dev/null +++ b/Zend/zend_class_alias.c @@ -0,0 +1,31 @@ +/* + +----------------------------------------------------------------------+ + | 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" +#include "zend.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; + alias->alias_flags = 0; + + return alias; +} diff --git a/Zend/zend_class_alias.h b/Zend/zend_class_alias.h new file mode 100644 index 0000000000000..e087332713ce0 --- /dev/null +++ b/Zend/zend_class_alias.h @@ -0,0 +1,54 @@ +/* + +----------------------------------------------------------------------+ + | 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; + uint32_t alias_flags; +}; + +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_compile.c b/Zend/zend_compile.c index 50ba8029873ad..458617ac5ea59 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; \ @@ -1867,8 +1868,13 @@ 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; + Z_CE_FROM_ZVAL_P(ce, ce_or_alias); cc = zend_hash_find_ptr(&ce->constants_table, name); } else { return 0; @@ -5449,7 +5455,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.h b/Zend/zend_execute.h index 8858f9fce96ae..6812783d0c6c8 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 660975f9bc1b5..472d8dab7a1f1 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" @@ -51,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; @@ -327,7 +328,12 @@ 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); + // CHECK + // if (Z_TYPE_P(zv) == IS_ALIAS_PTR) { + // continue; + // } + zend_class_entry *ce; + Z_CE_FROM_ZVAL_P(ce, zv); if (ce->default_static_members_count) { zend_cleanup_internal_class_data(ce); @@ -1206,7 +1212,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) && @@ -1273,7 +1279,17 @@ ZEND_API zend_class_entry *zend_lookup_class_ex(zend_string *name, zend_string * EG(filename_override) = NULL; EG(lineno_override) = -1; zend_exception_save(); - ce = zend_autoload(autoload_name, lc_name); + 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) { + 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; 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_opcode.c b/Zend/zend_opcode.c index 1962c7b5a56d1..759c07363a9c2 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" @@ -292,6 +293,21 @@ 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_P(zv) == IS_ALIAS_PTR)) { + zend_class_alias *class_alias = Z_CLASS_ALIAS_P(zv); + + if (class_alias->alias_flags & ZEND_ACC_IMMUTABLE) { + return; + } + + free(class_alias); + return; + } + zend_property_info *prop_info; zend_class_entry *ce = Z_PTR_P(zv); zend_function *fn; @@ -300,12 +316,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/Zend/zend_types.h b/Zend/zend_types.h index 43aa2aa86a00e..87d3c239fb847 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; @@ -1066,6 +1068,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) @@ -1278,7 +1283,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 77fc8c89cdf40..1762b4560afda 100644 --- a/configure.ac +++ b/configure.ac @@ -1740,6 +1740,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_attributes.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 e6a2b90e8fffc..2aba31c74f1d5 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" @@ -681,9 +682,8 @@ 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; - - ce = (zend_class_entry*)Z_PTR(p->val); + zend_class_entry *ce = NULL; + Z_CE_FROM_ZVAL(ce, p->val); if (p->key) { p->key = new_interned_string(p->key); @@ -3635,8 +3635,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; } @@ -3724,7 +3725,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) { @@ -3788,17 +3790,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++; } @@ -3837,9 +3849,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; @@ -3848,9 +3860,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; @@ -3860,9 +3872,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; @@ -4035,7 +4047,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) { @@ -4098,13 +4110,24 @@ static void preload_link(void) } zend_catch { /* Clear variance obligations that were left behind on bailout. */ if (CG(delayed_variance_obligations)) { - zend_hash_index_del( - CG(delayed_variance_obligations), (uintptr_t) Z_CE_P(zv)); + 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)); + } } /* 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); @@ -4126,8 +4149,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) { @@ -4151,7 +4173,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) { @@ -4205,7 +4227,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); @@ -4405,18 +4431,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); } @@ -4426,12 +4455,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(); @@ -4582,7 +4613,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 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-- 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); } } @@ -186,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); @@ -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); + const 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); @@ -337,15 +355,19 @@ static void zend_accel_do_delayed_early_binding( 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) { + 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; 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 d430f4833d3ca..1d415d2cf1b9b 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -782,6 +782,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)); @@ -1637,6 +1638,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; @@ -1662,6 +1664,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 ef69cceb0250b..334166ec3d7f9 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" @@ -912,6 +913,22 @@ 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) +{ + zend_class_alias *alias = orig_alias; + + alias = zend_shared_memdup_put(alias, sizeof(zend_class_alias)); + alias->ce = zend_persist_class_entry(alias->ce); + + if (EXPECTED(!ZCG(current_persistent_script)->corrupted)) { + alias->alias_flags |= ZEND_ACC_IMMUTABLE; + } else { + alias->alias_flags |= ZEND_ACC_FILE_CACHED; + } + + return alias; +} + zend_class_entry *zend_persist_class_entry(zend_class_entry *orig_ce) { Bucket *p; @@ -1333,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 c638d66619d0f..9490a592334c1 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) @@ -594,6 +595,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; @@ -602,7 +611,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 d6e55c982b425..d20f1ea347518 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" @@ -105,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; /* Exception throwing macro */ #define _DO_THROW(msg) \ @@ -1235,8 +1237,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) { @@ -5438,9 +5442,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; @@ -6817,7 +6823,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(); } @@ -6835,7 +6843,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(); } @@ -7897,6 +7907,76 @@ 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; 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)); + } + 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_string_release_ex(lc_name, /* persistent */ false); + 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); + + 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, __toString) +{ + reflection_object *intern; + zend_class_alias *alias; + smart_str str = {0}; + + ZEND_PARSE_PARAMETERS_NONE(); + + GET_REFLECTION_OBJECT_PTR(alias); + + 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)); +} + PHP_MINIT_FUNCTION(reflection) /* {{{ */ { memcpy(&reflection_object_handlers, &std_object_handlers, sizeof(zend_object_handlers)); @@ -8002,6 +8082,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) = 0; return SUCCESS; diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 91c70d6ffdb1a..11c4b8da9b271 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -930,3 +930,16 @@ 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 {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 62275423b3caf..e94ff425aff56 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 the .stub.php file instead. - * Stub hash: fd645a0b0db39d94ca25b39ffe64d7f05bad6bea */ + * Stub hash: 86a652853154073577402a480ed6c34d3f561822 */ ZEND_BEGIN_ARG_WITH_TENTATIVE_RETURN_TYPE_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 1, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0) @@ -723,6 +723,10 @@ 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 + ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); ZEND_METHOD(ReflectionFunctionAbstract, inNamespace); @@ -993,6 +997,8 @@ ZEND_METHOD(ReflectionConstant, getExtension); ZEND_METHOD(ReflectionConstant, getExtensionName); ZEND_METHOD(ReflectionConstant, __toString); ZEND_METHOD(ReflectionConstant, getAttributes); +ZEND_METHOD(ReflectionClassAlias, __construct); +ZEND_METHOD(ReflectionClassAlias, __toString); static const zend_function_entry class_Reflection_methods[] = { ZEND_ME(Reflection, getModifierNames, arginfo_class_Reflection_getModifierNames, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) @@ -1366,6 +1372,12 @@ 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_FE_END +}; + static zend_class_entry *register_class_ReflectionException(zend_class_entry *class_entry_Exception) { zend_class_entry ce, *class_entry; @@ -1903,3 +1915,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/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_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 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" + } } diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 0acfcbc5c16d0..2704bcce1f7cf 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -412,7 +412,7 @@ static bool autoload_func_info_equals( && alfi1->closure == alfi2->closure; } -static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { +static zval *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { if (!spl_autoload_functions) { return NULL; } @@ -442,13 +442,9 @@ static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_stri break; } - if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { - return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); - } else { - zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); - if (ce) { - return ce; - } + zval *ce_ptr = zend_hash_find(EG(class_table), lc_name); + if (ce_ptr) { + return ce_ptr; } zend_hash_move_forward_ex(spl_autoload_functions, &pos); diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..0e768ab359899 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_lazy_objects.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({