diff --git a/Zend/Optimizer/zend_optimizer.c b/Zend/Optimizer/zend_optimizer.c index 1c58d6b7372fb..92c48dc3d807b 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 @@ -773,7 +774,8 @@ void zend_optimizer_shift_jump(zend_op_array *op_array, zend_op *opline, uint32_ static bool zend_optimizer_ignore_class(zval *ce_zv, zend_string *filename) { - zend_class_entry *ce = Z_PTR_P(ce_zv); + zend_class_entry *ce; + Z_CE_FROM_ZVAL_P(ce, ce_zv); if (ce->ce_flags & ZEND_ACC_PRELOADED) { Bucket *ce_bucket = (Bucket*)((uintptr_t)ce_zv - XtOffsetOf(Bucket, val)); @@ -809,14 +811,22 @@ static bool zend_optimizer_ignore_function(zval *fbc_zv, zend_string *filename) 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)) { @@ -859,7 +869,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 e0006e7d7275f..09d9e69f5a55b 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 @@ -2543,7 +2544,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++; @@ -2557,7 +2560,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; @@ -3282,8 +3286,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) { - 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); } @@ -3596,7 +3601,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); @@ -3607,6 +3614,8 @@ ZEND_API zend_result zend_register_class_alias_ex(const char *name, size_t name_ } return SUCCESS; } + + free(alias); return FAILURE; } /* }}} */ @@ -3723,11 +3732,12 @@ ZEND_API zend_result zend_disable_class(const char *class_name, size_t class_nam key = zend_string_alloc(class_name_length, 0); zend_str_tolower_copy(ZSTR_VAL(key), class_name, class_name_length); - disabled_class = zend_hash_find_ptr(CG(class_table), key); + zval *disabled_class_or_alias = zend_hash_find(CG(class_table), key); zend_string_release_ex(key, 0); - if (!disabled_class) { + if (!disabled_class_or_alias) { return FAILURE; } + Z_CE_FROM_ZVAL_P(disabled_class, disabled_class_or_alias); /* Will be reset by INIT_CLASS_ENTRY. */ free(disabled_class->interfaces); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index 7a07ceadce2e2..c2e5077986454 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" @@ -1398,7 +1399,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 0669d106f15e9..de1c28310d457 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -38,6 +38,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; \ @@ -1875,8 +1876,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)) { - 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; @@ -5340,7 +5346,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 cf15c9e3b2db5..4d8dd3962d9d8 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 9a7803e44e66e..6bb952ded66fb 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); @@ -1201,7 +1207,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) && @@ -1268,7 +1274,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 15722eb6b2eb3..73712132082f8 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 6e7d31e15a40f..0c96532bcc89a 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" @@ -291,6 +292,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; @@ -299,12 +315,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 4a6d00b9d73ea..544ccad5c1077 100644 --- a/Zend/zend_types.h +++ b/Zend/zend_types.h @@ -99,6 +99,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 *); @@ -346,6 +347,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; @@ -1065,6 +1067,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) @@ -1277,7 +1282,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 e4bd8162a2ebc..fba51c381d717 100644 --- a/configure.ac +++ b/configure.ac @@ -1735,6 +1735,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 a2b2d0fde8b42..d1359baa0ae5a 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" @@ -680,9 +681,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); @@ -3620,8 +3620,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; } @@ -3709,7 +3710,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) { @@ -3773,17 +3775,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++; } @@ -3822,9 +3834,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; @@ -3833,9 +3845,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; @@ -3845,9 +3857,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; @@ -4020,7 +4032,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) { @@ -4083,13 +4095,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); @@ -4111,8 +4134,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) { @@ -4136,7 +4158,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) { @@ -4190,7 +4212,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); @@ -4370,18 +4396,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); } @@ -4391,12 +4420,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 21f056901fd1b..2b790bfd817df 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" @@ -132,10 +133,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); } } @@ -219,19 +224,32 @@ static zend_always_inline void _zend_accel_class_hash_copy(HashTable *target, Ha * value. */ continue; } else if (UNEXPECTED(!ZCG(accel_directives).ignore_dups)) { - 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); @@ -347,15 +365,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++) { 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) { + 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 fee90e42b574f..4a0a2f4e490c5 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -765,6 +765,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)); @@ -1619,6 +1620,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; @@ -1644,6 +1646,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 202cd73c90422..ed1338d90f49b 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" @@ -909,6 +910,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; @@ -1296,7 +1313,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 639d7d5446705..f1fe460d38791 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) @@ -593,6 +594,14 @@ void zend_persist_class_entry_calc(zend_class_entry *ce) } } +static void zend_persist_class_alias_entry_calc(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(HashTable *class_table) { Bucket *p; @@ -601,7 +610,12 @@ static void zend_accel_persist_class_table_calc(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 8ef0269481cf7..143ab124f268f 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) \ @@ -1217,8 +1219,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) { @@ -5430,9 +5434,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; @@ -6790,7 +6796,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, 1); } ZEND_HASH_FOREACH_END(); } @@ -6808,7 +6816,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, 0); } ZEND_HASH_FOREACH_END(); } @@ -7838,6 +7848,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)); @@ -7943,6 +8023,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 63518b446ad86..f0b7efac57ad4 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -926,3 +926,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 d50dc04ae3d15..907bc299f9c64 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: 7a8d126a96f0115783bd20a9adfc6bdc5ee88fda */ + * Stub hash: 8daecbf3ce2d4389db49cd6c5e58d7c62cae52c0 */ 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) @@ -721,6 +721,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); @@ -990,6 +994,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) @@ -1362,6 +1368,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; @@ -1905,3 +1917,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 1149be29bd46e..6d8ceff1340fa 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -414,7 +414,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; } @@ -444,13 +444,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 f509e4eae9337..e8b4381d306c5 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({