|
| 1 | +/* |
| 2 | + +----------------------------------------------------------------------+ |
| 3 | + | Zend Engine | |
| 4 | + +----------------------------------------------------------------------+ |
| 5 | + | Copyright (c) Zend Technologies Ltd. (http://www.zend.com) | |
| 6 | + +----------------------------------------------------------------------+ |
| 7 | + | This source file is subject to version 2.00 of the Zend license, | |
| 8 | + | that is bundled with this package in the file LICENSE, and is | |
| 9 | + | available through the world-wide-web at the following url: | |
| 10 | + | http://www.zend.com/license/2_00.txt. | |
| 11 | + | If you did not receive a copy of the Zend license and are unable to | |
| 12 | + | obtain it through the world-wide-web, please send a note to | |
| 13 | + | license@zend.com so we can mail you a copy immediately. | |
| 14 | + +----------------------------------------------------------------------+ |
| 15 | + | Authors: Gina Peter Banyard <girgias@php.net> | |
| 16 | + +----------------------------------------------------------------------+ |
| 17 | +*/ |
| 18 | + |
| 19 | +#include "zend.h" |
| 20 | +#include "zend_API.h" |
| 21 | +#include "zend_autoload.h" |
| 22 | +#include "zend_hash.h" |
| 23 | +#include "zend_types.h" |
| 24 | +#include "zend_exceptions.h" |
| 25 | +#include "zend_string.h" |
| 26 | + |
| 27 | +ZEND_TLS HashTable *zend_class_autoload_functions; |
| 28 | + |
| 29 | +static void zend_autoload_callback_zval_destroy(zval *element) |
| 30 | +{ |
| 31 | + zend_fcall_info_cache *fcc = Z_PTR_P(element); |
| 32 | + zend_fcc_dtor(fcc); |
| 33 | + efree(fcc); |
| 34 | +} |
| 35 | + |
| 36 | +static Bucket *autoload_find_registered_function(const HashTable *autoloader_table, const zend_fcall_info_cache *function_entry) |
| 37 | +{ |
| 38 | + zend_fcall_info_cache *current_function_entry; |
| 39 | + ZEND_HASH_MAP_FOREACH_PTR(autoloader_table, current_function_entry) { |
| 40 | + if (zend_fcc_equals(current_function_entry, function_entry)) { |
| 41 | + return _p; |
| 42 | + } |
| 43 | + } ZEND_HASH_FOREACH_END(); |
| 44 | + return NULL; |
| 45 | +} |
| 46 | + |
| 47 | +ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name) |
| 48 | +{ |
| 49 | + if (!zend_class_autoload_functions) { |
| 50 | + return NULL; |
| 51 | + } |
| 52 | + |
| 53 | + zval zname; |
| 54 | + ZVAL_STR(&zname, class_name); |
| 55 | + |
| 56 | + const HashTable *class_autoload_functions = zend_class_autoload_functions; |
| 57 | + |
| 58 | + /* Cannot use ZEND_HASH_MAP_FOREACH_PTR here as autoloaders may be |
| 59 | + * added/removed during autoloading. */ |
| 60 | + HashPosition pos; |
| 61 | + zend_hash_internal_pointer_reset_ex(class_autoload_functions, &pos); |
| 62 | + while (true) { |
| 63 | + zend_fcall_info_cache *func_info = zend_hash_get_current_data_ptr_ex(class_autoload_functions, &pos); |
| 64 | + if (!func_info) { |
| 65 | + break; |
| 66 | + } |
| 67 | + zend_call_known_fcc(func_info, /* retval */ NULL, /* param_count */ 1, /* params */ &zname, /* named_params */ NULL); |
| 68 | + |
| 69 | + if (EG(exception)) { |
| 70 | + return NULL; |
| 71 | + } |
| 72 | + if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) { |
| 73 | + return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name); |
| 74 | + } |
| 75 | + |
| 76 | + zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name); |
| 77 | + if (ce) { |
| 78 | + return ce; |
| 79 | + } |
| 80 | + |
| 81 | + zend_hash_move_forward_ex(class_autoload_functions, &pos); |
| 82 | + } |
| 83 | + return NULL; |
| 84 | +} |
| 85 | + |
| 86 | +/* Needed for compatibility with spl_register_autoload() */ |
| 87 | +ZEND_API void zend_autoload_register_class_loader(zend_fcall_info_cache *fcc, bool prepend) |
| 88 | +{ |
| 89 | + ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc)); |
| 90 | + |
| 91 | + if (!zend_class_autoload_functions) { |
| 92 | + ALLOC_HASHTABLE(zend_class_autoload_functions); |
| 93 | + zend_hash_init(zend_class_autoload_functions, 1, NULL, zend_autoload_callback_zval_destroy, false); |
| 94 | + /* Initialize as non-packed hash table for prepend functionality. */ |
| 95 | + zend_hash_real_init_mixed(zend_class_autoload_functions); |
| 96 | + } |
| 97 | + |
| 98 | + ZEND_ASSERT( |
| 99 | + fcc->function_handler->type != ZEND_INTERNAL_FUNCTION |
| 100 | + || !zend_string_equals_literal(fcc->function_handler->common.function_name, "spl_autoload_call") |
| 101 | + ); |
| 102 | + |
| 103 | + /* If function is already registered, don't do anything */ |
| 104 | + if (autoload_find_registered_function(zend_class_autoload_functions, fcc)) { |
| 105 | + /* Release potential call trampoline */ |
| 106 | + zend_release_fcall_info_cache(fcc); |
| 107 | + return; |
| 108 | + } |
| 109 | + |
| 110 | + zend_fcc_addref(fcc); |
| 111 | + zend_hash_next_index_insert_mem(zend_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache)); |
| 112 | + if (prepend && zend_hash_num_elements(zend_class_autoload_functions) > 1) { |
| 113 | + /* Move the newly created element to the head of the hashtable */ |
| 114 | + ZEND_ASSERT(!HT_IS_PACKED(zend_class_autoload_functions)); |
| 115 | + Bucket tmp = zend_class_autoload_functions->arData[zend_class_autoload_functions->nNumUsed-1]; |
| 116 | + memmove(zend_class_autoload_functions->arData + 1, zend_class_autoload_functions->arData, sizeof(Bucket) * (zend_class_autoload_functions->nNumUsed - 1)); |
| 117 | + zend_class_autoload_functions->arData[0] = tmp; |
| 118 | + zend_hash_rehash(zend_class_autoload_functions); |
| 119 | + } |
| 120 | +} |
| 121 | + |
| 122 | +ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc) { |
| 123 | + if (zend_class_autoload_functions) { |
| 124 | + Bucket *p = autoload_find_registered_function(zend_class_autoload_functions, fcc); |
| 125 | + if (p) { |
| 126 | + zend_hash_del_bucket(zend_class_autoload_functions, p); |
| 127 | + return true; |
| 128 | + } |
| 129 | + } |
| 130 | + return false; |
| 131 | +} |
| 132 | + |
| 133 | +/* We do not return a HashTable* because zend_empty_array is not collectable, |
| 134 | + * therefore the zval holding this value must do so. Something that ZVAL_EMPTY_ARRAY(); does. */ |
| 135 | +ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value) { |
| 136 | + if (zend_class_autoload_functions) { |
| 137 | + zend_fcall_info_cache *fcc; |
| 138 | + |
| 139 | + zend_array *map = zend_new_array(zend_hash_num_elements(zend_class_autoload_functions)); |
| 140 | + ZEND_HASH_MAP_FOREACH_PTR(zend_class_autoload_functions, fcc) { |
| 141 | + zval tmp; |
| 142 | + zend_get_callable_zval_from_fcc(fcc, &tmp); |
| 143 | + zend_hash_next_index_insert(map, &tmp); |
| 144 | + } ZEND_HASH_FOREACH_END(); |
| 145 | + RETURN_ARR(map); |
| 146 | + } |
| 147 | + RETURN_EMPTY_ARRAY(); |
| 148 | +} |
| 149 | + |
| 150 | +/* Only for deprecated strange behaviour of spl_autoload_unregister() */ |
| 151 | +ZEND_API void zend_autoload_clean_class_loaders(void) |
| 152 | +{ |
| 153 | + if (zend_class_autoload_functions) { |
| 154 | + /* Don't destroy the hash table, as we might be iterating over it right now. */ |
| 155 | + zend_hash_clean(zend_class_autoload_functions); |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +void zend_autoload_shutdown(void) |
| 160 | +{ |
| 161 | + if (zend_class_autoload_functions) { |
| 162 | + zend_hash_destroy(zend_class_autoload_functions); |
| 163 | + FREE_HASHTABLE(zend_class_autoload_functions); |
| 164 | + zend_class_autoload_functions = NULL; |
| 165 | + } |
| 166 | +} |
0 commit comments