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({