Coverage Report

Created: 2026-06-02 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
0
{
48
0
  if (!zend_class_autoload_functions) {
49
0
    return NULL;
50
0
  }
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
1.99k
{
160
1.99k
  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
1.99k
}