Coverage Report

Created: 2026-02-14 06:52

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 (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
462
{
31
462
  zend_fcall_info_cache *fcc = Z_PTR_P(element);
32
462
  zend_fcc_dtor(fcc);
33
462
  efree(fcc);
34
462
}
35
36
static Bucket *autoload_find_registered_function(const HashTable *autoloader_table, const zend_fcall_info_cache *function_entry)
37
472
{
38
472
  zend_fcall_info_cache *current_function_entry;
39
996
  ZEND_HASH_MAP_FOREACH_PTR(autoloader_table, current_function_entry) {
40
996
    if (zend_fcc_equals(current_function_entry, function_entry)) {
41
10
      return _p;
42
10
    }
43
996
  } ZEND_HASH_FOREACH_END();
44
462
  return NULL;
45
472
}
46
47
ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name)
48
117k
{
49
117k
  if (!zend_class_autoload_functions) {
50
116k
    return NULL;
51
116k
  }
52
53
994
  zval zname;
54
994
  ZVAL_STR(&zname, class_name);
55
56
994
  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
994
  HashPosition pos;
61
994
  zend_hash_internal_pointer_reset_ex(class_autoload_functions, &pos);
62
1.09k
  while (true) {
63
1.09k
    zend_fcall_info_cache *func_info = zend_hash_get_current_data_ptr_ex(class_autoload_functions, &pos);
64
1.09k
    if (!func_info) {
65
84
      break;
66
84
    }
67
1.00k
    zend_call_known_fcc(func_info, /* retval */ NULL, /* param_count */ 1, /* params */ &zname, /* named_params */ NULL);
68
69
1.00k
    if (EG(exception)) {
70
273
      return NULL;
71
273
    }
72
734
    if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) {
73
255
      return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name);
74
255
    }
75
76
479
    zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name);
77
479
    if (ce) {
78
241
      return ce;
79
241
    }
80
81
238
    zend_hash_move_forward_ex(class_autoload_functions, &pos);
82
238
  }
83
225
  return NULL;
84
994
}
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
472
{
89
472
  ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc));
90
91
472
  if (!zend_class_autoload_functions) {
92
446
    ALLOC_HASHTABLE(zend_class_autoload_functions);
93
446
    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
446
    zend_hash_real_init_mixed(zend_class_autoload_functions);
96
446
  }
97
98
472
  ZEND_ASSERT(
99
472
    fcc->function_handler->type != ZEND_INTERNAL_FUNCTION
100
472
    || !zend_string_equals_literal(fcc->function_handler->common.function_name, "spl_autoload_call")
101
472
  );
102
103
  /* If function is already registered, don't do anything */
104
472
  if (autoload_find_registered_function(zend_class_autoload_functions, fcc)) {
105
    /* Release potential call trampoline */
106
10
    zend_release_fcall_info_cache(fcc);
107
10
    return;
108
10
  }
109
110
462
  zend_fcc_addref(fcc);
111
462
  zend_hash_next_index_insert_mem(zend_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache));
112
462
  if (prepend && zend_hash_num_elements(zend_class_autoload_functions) > 1) {
113
    /* Move the newly created element to the head of the hashtable */
114
16
    ZEND_ASSERT(!HT_IS_PACKED(zend_class_autoload_functions));
115
16
    Bucket tmp = zend_class_autoload_functions->arData[zend_class_autoload_functions->nNumUsed-1];
116
16
    memmove(zend_class_autoload_functions->arData + 1, zend_class_autoload_functions->arData, sizeof(Bucket) * (zend_class_autoload_functions->nNumUsed - 1));
117
16
    zend_class_autoload_functions->arData[0] = tmp;
118
16
    zend_hash_rehash(zend_class_autoload_functions);
119
16
  }
120
462
}
121
122
0
ZEND_API bool zend_autoload_unregister_class_loader(const zend_fcall_info_cache *fcc) {
123
0
  if (zend_class_autoload_functions) {
124
0
    Bucket *p = autoload_find_registered_function(zend_class_autoload_functions, fcc);
125
0
    if (p) {
126
0
      zend_hash_del_bucket(zend_class_autoload_functions, p);
127
0
      return true;
128
0
    }
129
0
  }
130
0
  return false;
131
0
}
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
0
ZEND_API void zend_autoload_fcc_map_to_callable_zval_map(zval *return_value) {
136
0
  if (zend_class_autoload_functions) {
137
0
    zend_fcall_info_cache *fcc;
138
139
0
    zend_array *map = zend_new_array(zend_hash_num_elements(zend_class_autoload_functions));
140
0
    ZEND_HASH_MAP_FOREACH_PTR(zend_class_autoload_functions, fcc) {
141
0
      zval tmp;
142
0
      zend_get_callable_zval_from_fcc(fcc, &tmp);
143
0
      zend_hash_next_index_insert(map, &tmp);
144
0
    } ZEND_HASH_FOREACH_END();
145
0
    RETURN_ARR(map);
146
0
  }
147
0
  RETURN_EMPTY_ARRAY();
148
0
}
149
150
/* Only for deprecated strange behaviour of spl_autoload_unregister() */
151
ZEND_API void zend_autoload_clean_class_loaders(void)
152
0
{
153
0
  if (zend_class_autoload_functions) {
154
    /* Don't destroy the hash table, as we might be iterating over it right now. */
155
0
    zend_hash_clean(zend_class_autoload_functions);
156
0
  }
157
0
}
158
159
void zend_autoload_shutdown(void)
160
191k
{
161
191k
  if (zend_class_autoload_functions) {
162
446
    zend_hash_destroy(zend_class_autoload_functions);
163
446
    FREE_HASHTABLE(zend_class_autoload_functions);
164
    zend_class_autoload_functions = NULL;
165
446
  }
166
191k
}