From 03d0260ac8f83d04b6ef5d3ec004b6777ef3b649 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 24 Jan 2026 12:13:21 +0000 Subject: [PATCH 1/2] Zend: move class autoloading from SPL to Zend The primary motivation for this change is that this sort of functionality should reside in core and not in an extension. The reason being is that this causes issues in regard to extension dependencies and resolution, something that prevents GH-14544. --- Zend/zend_autoload.c | 165 ++++++++++++++++++++++++++++++++++ Zend/zend_autoload.h | 31 +++++++ Zend/zend_builtin_functions.c | 2 + Zend/zend_execute_API.c | 3 + configure.ac | 1 + ext/spl/php_spl.c | 120 ++----------------------- win32/build/config.w32 | 2 +- 7 files changed, 209 insertions(+), 115 deletions(-) create mode 100644 Zend/zend_autoload.c create mode 100644 Zend/zend_autoload.h diff --git a/Zend/zend_autoload.c b/Zend/zend_autoload.c new file mode 100644 index 0000000000000..89b8027aa339e --- /dev/null +++ b/Zend/zend_autoload.c @@ -0,0 +1,165 @@ +/* + +----------------------------------------------------------------------+ + | 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: Gina Peter Banyard | + +----------------------------------------------------------------------+ +*/ + +#include "zend.h" +#include "zend_API.h" +#include "zend_autoload.h" +#include "zend_hash.h" +#include "zend_types.h" +#include "zend_exceptions.h" +#include "zend_string.h" + +ZEND_TLS HashTable *autoloader_class_autoload_functions; + +ZEND_API void zend_autoload_callback_zval_destroy(zval *element) +{ + zend_fcall_info_cache *fcc = Z_PTR_P(element); + zend_fcc_dtor(fcc); + efree(fcc); +} + +static Bucket *autoload_find_registered_function(const HashTable *autoloader_table, const zend_fcall_info_cache *function_entry) +{ + zend_fcall_info_cache *current_function_entry; + ZEND_HASH_MAP_FOREACH_PTR(autoloader_table, current_function_entry) { + if (zend_fcc_equals(current_function_entry, function_entry)) { + return _p; + } + } ZEND_HASH_FOREACH_END(); + return NULL; +} + +ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name) +{ + if (!autoloader_class_autoload_functions) { + return NULL; + } + + zval zname; + ZVAL_STR(&zname, class_name); + + const HashTable *class_autoload_functions = autoloader_class_autoload_functions; + + /* Cannot use ZEND_HASH_MAP_FOREACH_PTR here as autoloaders may be + * added/removed during autoloading. */ + HashPosition pos; + zend_hash_internal_pointer_reset_ex(class_autoload_functions, &pos); + while (true) { + zend_fcall_info_cache *func_info = zend_hash_get_current_data_ptr_ex(class_autoload_functions, &pos); + if (!func_info) { + break; + } + zend_call_known_fcc(func_info, /* retval */ NULL, /* param_count */ 1, /* params */ &zname, /* named_params */ NULL); + + if (EG(exception)) { + return NULL; + } + if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { + return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); + } + + zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce) { + return ce; + } + + zend_hash_move_forward_ex(class_autoload_functions, &pos); + } + return NULL; +} +/* Needed for compatibility with spl_register_autoload() */ +ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend) +{ + ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc)); + + if (!autoloader_class_autoload_functions) { + ALLOC_HASHTABLE(autoloader_class_autoload_functions); + zend_hash_init(autoloader_class_autoload_functions, 1, NULL, zend_autoload_callback_zval_destroy, false); + /* Initialize as non-packed hash table for prepend functionality. */ + zend_hash_real_init_mixed(autoloader_class_autoload_functions); + } + + // TODO: Assertion for this + //if (fcc->function_handler->type == ZEND_INTERNAL_FUNCTION && + // fcc->function_handler->internal_function.handler == zif_autoload_call_class) { + // zend_argument_value_error(1, "must not be the autoload_call_class() function"); + // return; + //} + + /* If function is already registered, don't do anything */ + if (autoload_find_registered_function(autoloader_class_autoload_functions, fcc)) { + /* Release potential call trampoline */ + zend_release_fcall_info_cache(fcc); + return; + } + + zend_fcc_addref(fcc); + zend_hash_next_index_insert_mem(autoloader_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache)); + if (prepend && zend_hash_num_elements(autoloader_class_autoload_functions) > 1) { + /* Move the newly created element to the head of the hashtable */ + ZEND_ASSERT(!HT_IS_PACKED(autoloader_class_autoload_functions)); + Bucket tmp = autoloader_class_autoload_functions->arData[autoloader_class_autoload_functions->nNumUsed-1]; + memmove(autoloader_class_autoload_functions->arData + 1, autoloader_class_autoload_functions->arData, sizeof(Bucket) * (autoloader_class_autoload_functions->nNumUsed - 1)); + autoloader_class_autoload_functions->arData[0] = tmp; + zend_hash_rehash(autoloader_class_autoload_functions); + } +} + +ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc) { + if (autoloader_class_autoload_functions) { + Bucket *p = autoload_find_registered_function(autoloader_class_autoload_functions, fcc); + if (p) { + zend_hash_del_bucket(autoloader_class_autoload_functions, p); + return true; + } + } + return false; +} + +ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value) { + if (autoloader_class_autoload_functions) { + zend_fcall_info_cache *fcc; + + array_init_size(return_value, zend_hash_num_elements(autoloader_class_autoload_functions)); + ZEND_HASH_MAP_FOREACH_PTR(autoloader_class_autoload_functions, fcc) { + zval tmp; + zend_get_callable_zval_from_fcc(fcc, &tmp); + add_next_index_zval(return_value, &tmp); + } ZEND_HASH_FOREACH_END(); + } else { + RETURN_EMPTY_ARRAY(); + } +} + +/* Only for deprecated strange behaviour of spl_autoload_unregister() */ +ZEND_API void zend_autoload_drop_autoload_map(void) +{ + if (autoloader_class_autoload_functions) { + /* Don't destroy the hash table, as we might be iterating over it right now. */ + zend_hash_clean(autoloader_class_autoload_functions); + } +} + +void zend_autoload_shutdown(void) +{ + if (autoloader_class_autoload_functions) { + zend_hash_destroy(autoloader_class_autoload_functions); + FREE_HASHTABLE(autoloader_class_autoload_functions); + autoloader_class_autoload_functions = NULL; + } +} diff --git a/Zend/zend_autoload.h b/Zend/zend_autoload.h new file mode 100644 index 0000000000000..aa78f0002cb18 --- /dev/null +++ b/Zend/zend_autoload.h @@ -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: Gina Peter Banyard | + +----------------------------------------------------------------------+ +*/ + +#include "zend_string.h" +#include "zend_API.h" +#include "zend.h" + +ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name); +ZEND_API zend_class_entry *zend_perform_class_map_autoload(zend_string *class_name, zend_string *lc_name); +ZEND_API void zend_autoload_callback_zval_destroy(zval *entry); +ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend); +ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc); +ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value); +/* Only for deprecated strange behaviour of spl_autoload_unregister() */ +ZEND_API void zend_autoload_drop_autoload_map(void); +void zend_autoload_shutdown(void); diff --git a/Zend/zend_builtin_functions.c b/Zend/zend_builtin_functions.c index a4ece3061d5d0..09d8793fd56ec 100644 --- a/Zend/zend_builtin_functions.c +++ b/Zend/zend_builtin_functions.c @@ -29,12 +29,14 @@ #include "zend_extensions.h" #include "zend_closures.h" #include "zend_generators.h" +#include "zend_autoload.h" #include "zend_builtin_functions_arginfo.h" #include "zend_smart_str.h" /* }}} */ ZEND_MINIT_FUNCTION(core) { /* {{{ */ + zend_autoload = zend_perform_class_autoload; zend_register_default_classes(); zend_standard_class_def = register_class_stdClass(); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index 69337e27fd530..efb76441b35cd 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -39,6 +39,7 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_frameless_function.h" +#include "zend_autoload.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -451,6 +452,8 @@ void shutdown_executor(void) /* {{{ */ zend_stream_shutdown(); } zend_end_try(); + /* Shutdown autoloader prior to releasing values as it may hold references to objects */ + zend_autoload_shutdown(); zend_shutdown_executor_values(fast_shutdown); zend_weakrefs_shutdown(); diff --git a/configure.ac b/configure.ac index 77fc8c89cdf40..beeed7eb21115 100644 --- a/configure.ac +++ b/configure.ac @@ -1738,6 +1738,7 @@ PHP_ADD_SOURCES([Zend], m4_normalize([ zend_ast.c zend_atomic.c zend_attributes.c + zend_autoload.c zend_builtin_functions.c zend_call_stack.c zend_closures.c diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index 35cc7d426911d..d57a1a5e53b76 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -32,11 +32,11 @@ #include "spl_dllist.h" #include "spl_fixedarray.h" #include "spl_heap.h" +#include "zend_autoload.h" #include "zend_exceptions.h" #include "zend_interfaces.h" ZEND_TLS zend_string *spl_autoload_extensions; -ZEND_TLS HashTable *spl_autoload_functions; #define SPL_DEFAULT_FILE_EXTENSIONS ".inc,.php" @@ -340,50 +340,6 @@ PHP_FUNCTION(spl_autoload_extensions) } } /* }}} */ -static void autoload_func_info_zval_dtor(zval *element) -{ - zend_fcall_info_cache *fcc = Z_PTR_P(element); - zend_fcc_dtor(fcc); - efree(fcc); -} - -static zend_class_entry *spl_perform_autoload(zend_string *class_name, zend_string *lc_name) { - if (!spl_autoload_functions) { - return NULL; - } - - /* We don't use ZEND_HASH_MAP_FOREACH here, - * because autoloaders may be added/removed during autoloading. */ - HashPosition pos; - zend_hash_internal_pointer_reset_ex(spl_autoload_functions, &pos); - - zval zname; - ZVAL_STR(&zname, class_name); - while (true) { - zend_fcall_info_cache *fcc = - zend_hash_get_current_data_ptr_ex(spl_autoload_functions, &pos); - if (!fcc) { - break; - } - - zend_call_known_fcc(fcc, NULL, 1, &zname, NULL); - if (UNEXPECTED(EG(exception))) { - break; - } - - if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { - return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); - } - zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); - if (ce != NULL) { - return ce; - } - - zend_hash_move_forward_ex(spl_autoload_functions, &pos); - } - return NULL; -} - /* {{{ Try all registered autoload function to load the requested class */ PHP_FUNCTION(spl_autoload_call) { @@ -394,24 +350,10 @@ PHP_FUNCTION(spl_autoload_call) } zend_string *lc_name = zend_string_tolower(class_name); - spl_perform_autoload(class_name, lc_name); + zend_perform_class_autoload(class_name, lc_name); zend_string_release(lc_name); } /* }}} */ -static Bucket *spl_find_registered_function(const zend_fcall_info_cache *find_fcc) { - if (!spl_autoload_functions) { - return NULL; - } - - zend_fcall_info_cache *fcc; - ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, fcc) { - if (zend_fcc_equals(fcc, find_fcc)) { - return _p; - } - } ZEND_HASH_FOREACH_END(); - return NULL; -} - /* {{{ Register given function as autoloader */ PHP_FUNCTION(spl_autoload_register) { @@ -432,13 +374,6 @@ PHP_FUNCTION(spl_autoload_register) "spl_autoload_register() will always throw"); } - if (!spl_autoload_functions) { - ALLOC_HASHTABLE(spl_autoload_functions); - zend_hash_init(spl_autoload_functions, 1, NULL, autoload_func_info_zval_dtor, false); - /* Initialize as non-packed hash table for prepend functionality. */ - zend_hash_real_init_mixed(spl_autoload_functions); - } - /* If first arg is not null */ if (ZEND_FCI_INITIALIZED(fci)) { if (!ZEND_FCC_INITIALIZED(fcc)) { @@ -458,22 +393,7 @@ PHP_FUNCTION(spl_autoload_register) fcc.function_handler = zend_hash_str_find_ptr(CG(function_table), ZEND_STRL("spl_autoload")); } - if (spl_find_registered_function(&fcc)) { - /* Release call trampoline */ - zend_release_fcall_info_cache(&fcc); - RETURN_TRUE; - } - - zend_fcc_addref(&fcc); - zend_hash_next_index_insert_mem(spl_autoload_functions, &fcc, sizeof(zend_fcall_info_cache)); - if (prepend && spl_autoload_functions->nNumOfElements > 1) { - /* Move the newly created element to the head of the hashtable */ - ZEND_ASSERT(!HT_IS_PACKED(spl_autoload_functions)); - Bucket tmp = spl_autoload_functions->arData[spl_autoload_functions->nNumUsed-1]; - memmove(spl_autoload_functions->arData + 1, spl_autoload_functions->arData, sizeof(Bucket) * (spl_autoload_functions->nNumUsed - 1)); - spl_autoload_functions->arData[0] = tmp; - zend_hash_rehash(spl_autoload_functions); - } + zend_autoload_register_class_loader(&fcc, prepend); RETURN_TRUE; } /* }}} */ @@ -498,22 +418,13 @@ PHP_FUNCTION(spl_autoload_unregister) if (UNEXPECTED(EG(exception))) { RETURN_THROWS(); } - if (spl_autoload_functions) { - /* Don't destroy the hash table, as we might be iterating over it right now. */ - zend_hash_clean(spl_autoload_functions); - } + zend_autoload_drop_autoload_map(); RETURN_TRUE; } - Bucket *p = spl_find_registered_function(&fcc); + RETVAL_BOOL(zend_autoload_unregister_class_loader(&fcc)); /* Release trampoline */ zend_release_fcall_info_cache(&fcc); - if (p) { - zend_hash_del_bucket(spl_autoload_functions, p); - RETURN_TRUE; - } - - RETURN_FALSE; } /* }}} */ /* {{{ Return all registered autoloader functions */ @@ -521,18 +432,7 @@ PHP_FUNCTION(spl_autoload_functions) { ZEND_PARSE_PARAMETERS_NONE(); - if (spl_autoload_functions) { - zend_fcall_info_cache *fcc; - - array_init_size(return_value, zend_hash_num_elements(spl_autoload_functions)); - ZEND_HASH_MAP_FOREACH_PTR(spl_autoload_functions, fcc) { - zval tmp; - zend_get_callable_zval_from_fcc(fcc, &tmp); - add_next_index_zval(return_value, &tmp); - } ZEND_HASH_FOREACH_END(); - } else { - RETURN_EMPTY_ARRAY(); - } + zend_autoload_fcc_map_to_callable_zval_map(return_value); } /* }}} */ /* {{{ Return hash id for given object */ @@ -612,8 +512,6 @@ PHP_MINFO_FUNCTION(spl) /* {{{ PHP_MINIT_FUNCTION(spl) */ PHP_MINIT_FUNCTION(spl) { - zend_autoload = spl_perform_autoload; - PHP_MINIT(spl_exceptions)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_iterators)(INIT_FUNC_ARGS_PASSTHRU); PHP_MINIT(spl_array)(INIT_FUNC_ARGS_PASSTHRU); @@ -630,7 +528,6 @@ PHP_MINIT_FUNCTION(spl) PHP_RINIT_FUNCTION(spl) /* {{{ */ { spl_autoload_extensions = NULL; - spl_autoload_functions = NULL; return SUCCESS; } /* }}} */ @@ -640,11 +537,6 @@ PHP_RSHUTDOWN_FUNCTION(spl) /* {{{ */ zend_string_release_ex(spl_autoload_extensions, 0); spl_autoload_extensions = NULL; } - if (spl_autoload_functions) { - zend_hash_destroy(spl_autoload_functions); - FREE_HASHTABLE(spl_autoload_functions); - spl_autoload_functions = NULL; - } return SUCCESS; } /* }}} */ diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 403f0aa6efbfe..aefcfb5f82474 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_autoload.c"); ADD_SOURCES("Zend\\Optimizer", "zend_optimizer.c pass1.c pass3.c optimize_func_calls.c block_pass.c optimize_temp_vars_5.c nop_removal.c compact_literals.c zend_cfg.c zend_dfg.c dfa_pass.c zend_ssa.c zend_inference.c zend_func_info.c zend_call_graph.c zend_dump.c escape_analysis.c compact_vars.c dce.c sccp.c scdf.c"); var PHP_ASSEMBLER = PATH_PROG({ From 7480341776ed6d93b13d4fbbb6a2a2953a7c0579 Mon Sep 17 00:00:00 2001 From: Gina Peter Banyard Date: Sat, 24 Jan 2026 17:36:01 +0000 Subject: [PATCH 2/2] First pass of review comments --- Zend/zend_autoload.c | 77 ++++++++++++++++++++--------------------- Zend/zend_autoload.h | 7 ++-- Zend/zend_execute_API.c | 3 -- ext/spl/php_spl.c | 4 +-- main/main.c | 23 +++++++----- 5 files changed, 57 insertions(+), 57 deletions(-) diff --git a/Zend/zend_autoload.c b/Zend/zend_autoload.c index 89b8027aa339e..5d90e9d2d468a 100644 --- a/Zend/zend_autoload.c +++ b/Zend/zend_autoload.c @@ -24,9 +24,9 @@ #include "zend_exceptions.h" #include "zend_string.h" -ZEND_TLS HashTable *autoloader_class_autoload_functions; +ZEND_TLS HashTable *zend_class_autoload_functions; -ZEND_API void zend_autoload_callback_zval_destroy(zval *element) +static void zend_autoload_callback_zval_destroy(zval *element) { zend_fcall_info_cache *fcc = Z_PTR_P(element); zend_fcc_dtor(fcc); @@ -46,14 +46,14 @@ static Bucket *autoload_find_registered_function(const HashTable *autoloader_tab ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name) { - if (!autoloader_class_autoload_functions) { + if (!zend_class_autoload_functions) { return NULL; } zval zname; ZVAL_STR(&zname, class_name); - const HashTable *class_autoload_functions = autoloader_class_autoload_functions; + const HashTable *class_autoload_functions = zend_class_autoload_functions; /* Cannot use ZEND_HASH_MAP_FOREACH_PTR here as autoloaders may be * added/removed during autoloading. */ @@ -82,84 +82,83 @@ ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, } return NULL; } + /* Needed for compatibility with spl_register_autoload() */ ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend) { ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc)); - if (!autoloader_class_autoload_functions) { - ALLOC_HASHTABLE(autoloader_class_autoload_functions); - zend_hash_init(autoloader_class_autoload_functions, 1, NULL, zend_autoload_callback_zval_destroy, false); + if (!zend_class_autoload_functions) { + ALLOC_HASHTABLE(zend_class_autoload_functions); + zend_hash_init(zend_class_autoload_functions, 1, NULL, zend_autoload_callback_zval_destroy, false); /* Initialize as non-packed hash table for prepend functionality. */ - zend_hash_real_init_mixed(autoloader_class_autoload_functions); + zend_hash_real_init_mixed(zend_class_autoload_functions); } - // TODO: Assertion for this - //if (fcc->function_handler->type == ZEND_INTERNAL_FUNCTION && - // fcc->function_handler->internal_function.handler == zif_autoload_call_class) { - // zend_argument_value_error(1, "must not be the autoload_call_class() function"); - // return; - //} + ZEND_ASSERT( + fcc->function_handler->type != ZEND_INTERNAL_FUNCTION + || !zend_string_equals_literal(fcc->function_handler->common.function_name, "spl_autoload_call") + ); /* If function is already registered, don't do anything */ - if (autoload_find_registered_function(autoloader_class_autoload_functions, fcc)) { + if (autoload_find_registered_function(zend_class_autoload_functions, fcc)) { /* Release potential call trampoline */ zend_release_fcall_info_cache(fcc); return; } zend_fcc_addref(fcc); - zend_hash_next_index_insert_mem(autoloader_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache)); - if (prepend && zend_hash_num_elements(autoloader_class_autoload_functions) > 1) { + zend_hash_next_index_insert_mem(zend_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache)); + if (prepend && zend_hash_num_elements(zend_class_autoload_functions) > 1) { /* Move the newly created element to the head of the hashtable */ - ZEND_ASSERT(!HT_IS_PACKED(autoloader_class_autoload_functions)); - Bucket tmp = autoloader_class_autoload_functions->arData[autoloader_class_autoload_functions->nNumUsed-1]; - memmove(autoloader_class_autoload_functions->arData + 1, autoloader_class_autoload_functions->arData, sizeof(Bucket) * (autoloader_class_autoload_functions->nNumUsed - 1)); - autoloader_class_autoload_functions->arData[0] = tmp; - zend_hash_rehash(autoloader_class_autoload_functions); + ZEND_ASSERT(!HT_IS_PACKED(zend_class_autoload_functions)); + Bucket tmp = zend_class_autoload_functions->arData[zend_class_autoload_functions->nNumUsed-1]; + memmove(zend_class_autoload_functions->arData + 1, zend_class_autoload_functions->arData, sizeof(Bucket) * (zend_class_autoload_functions->nNumUsed - 1)); + zend_class_autoload_functions->arData[0] = tmp; + zend_hash_rehash(zend_class_autoload_functions); } } ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc) { - if (autoloader_class_autoload_functions) { - Bucket *p = autoload_find_registered_function(autoloader_class_autoload_functions, fcc); + if (zend_class_autoload_functions) { + Bucket *p = autoload_find_registered_function(zend_class_autoload_functions, fcc); if (p) { - zend_hash_del_bucket(autoloader_class_autoload_functions, p); + zend_hash_del_bucket(zend_class_autoload_functions, p); return true; } } return false; } -ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value) { - if (autoloader_class_autoload_functions) { +ZEND_API zend_array* zend_autoload_fcc_map_to_callable_zval_map(void) { + if (zend_class_autoload_functions) { zend_fcall_info_cache *fcc; - array_init_size(return_value, zend_hash_num_elements(autoloader_class_autoload_functions)); - ZEND_HASH_MAP_FOREACH_PTR(autoloader_class_autoload_functions, fcc) { + zend_array *map = zend_new_array(zend_hash_num_elements(zend_class_autoload_functions)); + ZEND_HASH_MAP_FOREACH_PTR(zend_class_autoload_functions, fcc) { zval tmp; zend_get_callable_zval_from_fcc(fcc, &tmp); - add_next_index_zval(return_value, &tmp); + zend_hash_next_index_insert(map, &tmp); } ZEND_HASH_FOREACH_END(); - } else { - RETURN_EMPTY_ARRAY(); + return map; } + return (zend_array*)&zend_empty_array; } /* Only for deprecated strange behaviour of spl_autoload_unregister() */ -ZEND_API void zend_autoload_drop_autoload_map(void) +ZEND_API void zend_autoload_clean_class_loaders(void) { - if (autoloader_class_autoload_functions) { + if (zend_class_autoload_functions) { /* Don't destroy the hash table, as we might be iterating over it right now. */ - zend_hash_clean(autoloader_class_autoload_functions); + zend_hash_clean(zend_class_autoload_functions); } } void zend_autoload_shutdown(void) { - if (autoloader_class_autoload_functions) { - zend_hash_destroy(autoloader_class_autoload_functions); - FREE_HASHTABLE(autoloader_class_autoload_functions); - autoloader_class_autoload_functions = NULL; + if (zend_class_autoload_functions) { + zend_hash_destroy(zend_class_autoload_functions); + FREE_HASHTABLE(zend_class_autoload_functions); + zend_class_autoload_functions = NULL; } } diff --git a/Zend/zend_autoload.h b/Zend/zend_autoload.h index aa78f0002cb18..dadca853ceb90 100644 --- a/Zend/zend_autoload.h +++ b/Zend/zend_autoload.h @@ -17,15 +17,14 @@ */ #include "zend_string.h" +#include "zend_hash.h" #include "zend_API.h" #include "zend.h" ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name); -ZEND_API zend_class_entry *zend_perform_class_map_autoload(zend_string *class_name, zend_string *lc_name); -ZEND_API void zend_autoload_callback_zval_destroy(zval *entry); ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend); ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc); -ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value); +ZEND_API zend_array* zend_autoload_fcc_map_to_callable_zval_map(void); /* Only for deprecated strange behaviour of spl_autoload_unregister() */ -ZEND_API void zend_autoload_drop_autoload_map(void); +ZEND_API void zend_autoload_clean_class_loaders(void); void zend_autoload_shutdown(void); diff --git a/Zend/zend_execute_API.c b/Zend/zend_execute_API.c index efb76441b35cd..69337e27fd530 100644 --- a/Zend/zend_execute_API.c +++ b/Zend/zend_execute_API.c @@ -39,7 +39,6 @@ #include "zend_observer.h" #include "zend_call_stack.h" #include "zend_frameless_function.h" -#include "zend_autoload.h" #ifdef HAVE_SYS_TIME_H #include #endif @@ -452,8 +451,6 @@ void shutdown_executor(void) /* {{{ */ zend_stream_shutdown(); } zend_end_try(); - /* Shutdown autoloader prior to releasing values as it may hold references to objects */ - zend_autoload_shutdown(); zend_shutdown_executor_values(fast_shutdown); zend_weakrefs_shutdown(); diff --git a/ext/spl/php_spl.c b/ext/spl/php_spl.c index d57a1a5e53b76..2bc72a02d3460 100644 --- a/ext/spl/php_spl.c +++ b/ext/spl/php_spl.c @@ -418,7 +418,7 @@ PHP_FUNCTION(spl_autoload_unregister) if (UNEXPECTED(EG(exception))) { RETURN_THROWS(); } - zend_autoload_drop_autoload_map(); + zend_autoload_clean_class_loaders(); RETURN_TRUE; } @@ -432,7 +432,7 @@ PHP_FUNCTION(spl_autoload_functions) { ZEND_PARSE_PARAMETERS_NONE(); - zend_autoload_fcc_map_to_callable_zval_map(return_value); + RETURN_ARR(zend_autoload_fcc_map_to_callable_zval_map()); } /* }}} */ /* {{{ Return hash id for given object */ diff --git a/main/main.c b/main/main.c index f190eab3d094f..1538017db1a6f 100644 --- a/main/main.c +++ b/main/main.c @@ -23,6 +23,8 @@ #include "php.h" #include #include + +#include "zend_autoload.h" #ifdef PHP_WIN32 #include "win32/time.h" #include "win32/signal.h" @@ -2012,7 +2014,10 @@ void php_request_shutdown(void *dummy) php_free_shutdown_functions(); } - /* 8. Destroy super-globals */ + /* 8. Shutdown autoloader, freeing all hold functions/closures */ + zend_autoload_shutdown(); + + /* 9. Destroy super-globals */ zend_try { int i; @@ -2021,33 +2026,33 @@ void php_request_shutdown(void *dummy) } } zend_end_try(); - /* 9. Shutdown scanner/executor/compiler and restore ini entries */ + /* 10. Shutdown scanner/executor/compiler and restore ini entries */ zend_deactivate(); - /* 10. free request-bound globals */ + /* 11. free request-bound globals */ php_free_request_globals(); - /* 11. Call all extensions post-RSHUTDOWN functions */ + /* 12. Call all extensions post-RSHUTDOWN functions */ zend_try { zend_post_deactivate_modules(); } zend_end_try(); - /* 12. SAPI related shutdown*/ + /* 13. SAPI related shutdown*/ zend_try { sapi_deactivate_module(); } zend_end_try(); /* free SAPI stuff */ sapi_deactivate_destroy(); - /* 13. free virtual CWD memory */ + /* 14. free virtual CWD memory */ virtual_cwd_deactivate(); - /* 14. Destroy stream hashes */ + /* 15. Destroy stream hashes */ zend_try { php_shutdown_stream_hashes(); } zend_end_try(); - /* 15. Free Willy (here be crashes) */ + /* 16. Free Willy (here be crashes) */ zend_arena_destroy(CG(arena)); zend_interned_strings_deactivate(); zend_try { @@ -2058,7 +2063,7 @@ void php_request_shutdown(void *dummy) * At this point, no memory beyond a single chunk should be in use. */ zend_set_memory_limit(PG(memory_limit)); - /* 16. Deactivate Zend signals */ + /* 17. Deactivate Zend signals */ #ifdef ZEND_SIGNALS zend_signal_deactivate(); #endif