Coverage Report

Created: 2026-02-09 07:07

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
486
{
31
486
  zend_fcall_info_cache *fcc = Z_PTR_P(element);
32
486
  zend_fcc_dtor(fcc);
33
486
  efree(fcc);
34
486
}
35
36
static Bucket *autoload_find_registered_function(const HashTable *autoloader_table, const zend_fcall_info_cache *function_entry)
37
496
{
38
496
  zend_fcall_info_cache *current_function_entry;
39
1.04k
  ZEND_HASH_MAP_FOREACH_PTR(autoloader_table, current_function_entry) {
40
1.04k
    if (zend_fcc_equals(current_function_entry, function_entry)) {
41
10
      return _p;
42
10
    }
43
1.04k
  } ZEND_HASH_FOREACH_END();
44
486
  return NULL;
45
496
}
46
47
ZEND_API zend_class_entry *zend_perform_class_autoload(zend_string *class_name, zend_string *lc_name)
48
160k
{
49
160k
  if (!zend_class_autoload_functions) {
50
158k
    return NULL;
51
158k
  }
52
53
1.04k
  zval zname;
54
1.04k
  ZVAL_STR(&zname, class_name);
55
56
1.04k
  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
1.04k
  HashPosition pos;
61
1.04k
  zend_hash_internal_pointer_reset_ex(class_autoload_functions, &pos);
62
1.15k
  while (true) {
63
1.15k
    zend_fcall_info_cache *func_info = zend_hash_get_current_data_ptr_ex(class_autoload_functions, &pos);
64
1.15k
    if (!func_info) {
65
92
      break;
66
92
    }
67
1.05k
    zend_call_known_fcc(func_info, /* retval */ NULL, /* param_count */ 1, /* params */ &zname, /* named_params */ NULL);
68
69
1.05k
    if (EG(exception)) {
70
285
      return NULL;
71
285
    }
72
774
    if (ZSTR_HAS_CE_CACHE(class_name) && ZSTR_GET_CE_CACHE(class_name)) {
73
265
      return (zend_class_entry*)ZSTR_GET_CE_CACHE(class_name);
74
265
    }
75
76
509
    zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), lc_name);
77
509
    if (ce) {
78
256
      return ce;
79
256
    }
80
81
253
    zend_hash_move_forward_ex(class_autoload_functions, &pos);
82
253
  }
83
238
  return NULL;
84
1.04k
}
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
496
{
89
496
  ZEND_ASSERT(ZEND_FCC_INITIALIZED(*fcc));
90
91
496
  if (!zend_class_autoload_functions) {
92
468
    ALLOC_HASHTABLE(zend_class_autoload_functions);
93
468
    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
468
    zend_hash_real_init_mixed(zend_class_autoload_functions);
96
468
  }
97
98
496
  ZEND_ASSERT(
99
496
    fcc->function_handler->type != ZEND_INTERNAL_FUNCTION
100
496
    || !zend_string_equals_literal(fcc->function_handler->common.function_name, "spl_autoload_call")
101
496
  );
102
103
  /* If function is already registered, don't do anything */
104
496
  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
486
  zend_fcc_addref(fcc);
111
486
  zend_hash_next_index_insert_mem(zend_class_autoload_functions, fcc, sizeof(zend_fcall_info_cache));
112
486
  if (prepend && zend_hash_num_elements(zend_class_autoload_functions) > 1) {
113
    /* Move the newly created element to the head of the hashtable */
114
18
    ZEND_ASSERT(!HT_IS_PACKED(zend_class_autoload_functions));
115
18
    Bucket tmp = zend_class_autoload_functions->arData[zend_class_autoload_functions->nNumUsed-1];
116
18
    memmove(zend_class_autoload_functions->arData + 1, zend_class_autoload_functions->arData, sizeof(Bucket) * (zend_class_autoload_functions->nNumUsed - 1));
117
18
    zend_class_autoload_functions->arData[0] = tmp;
118
18
    zend_hash_rehash(zend_class_autoload_functions);
119
18
  }
120
486
}
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
244k
{
161
244k
  if (zend_class_autoload_functions) {
162
468
    zend_hash_destroy(zend_class_autoload_functions);
163
468
    FREE_HASHTABLE(zend_class_autoload_functions);
164
    zend_class_autoload_functions = NULL;
165
468
  }
166
244k
}