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