• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

krakjoe / uopz / 11987054034

23 Nov 2024 12:15PM UTC coverage: 95.502%. Remained the same
11987054034

Pull #185

github

web-flow
Merge 5af3a2887 into dcb01f128
Pull Request #185: Support `exit()` function in PHP 8.4

913 of 956 relevant lines covered (95.5%)

9208.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.3
/src/function.c
1
/*
2
  +----------------------------------------------------------------------+
3
  | uopz                                                                 |
4
  +----------------------------------------------------------------------+
5
  | Copyright (c) Joe Watkins 2016-2021                                  |
6
  +----------------------------------------------------------------------+
7
  | This source file is subject to version 3.01 of the PHP 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.php.net/license/3_01.txt                                  |
11
  | If you did not receive a copy of the PHP license and are unable to   |
12
  | obtain it through the world-wide-web, please send a note to          |
13
  | license@php.net so we can mail you a copy immediately.               |
14
  +----------------------------------------------------------------------+
15
  | Author: Joe Watkins <krakjoe@php.net>                                |
16
  +----------------------------------------------------------------------+
17
 */
18

19
#ifndef UOPZ_FUNCTION
20
#define UOPZ_FUNCTION
21

22
#include "php.h"
23
#include "uopz.h"
24

25
#include "util.h"
26
#include "function.h"
27

28
#include <Zend/zend_closures.h>
29

30
#ifndef ZEND_EXIT
31
#include "zend.h"
32
#include <Zend/zend_attributes.h>
33
#include <Zend/zend_builtin_functions_arginfo.h>
34
#endif        /* ZEND_EXIT */
35

36
ZEND_EXTERN_MODULE_GLOBALS(uopz);
37

38
static zend_function* uopz_copy_function(zend_class_entry *scope, zend_string *name, zend_object *closure, zend_long flags) { /* {{{ */
208✔
39
        zend_function  *copy = (zend_function*) zend_arena_alloc(&CG(arena), sizeof(zend_op_array));
208✔
40

41
        memcpy(copy, zend_get_closure_method_def(closure), sizeof(zend_op_array));
208✔
42

43
        copy->op_array.fn_flags &= ~ZEND_ACC_CLOSURE|ZEND_ACC_IMMUTABLE|ZEND_ACC_ARENA_ALLOCATED;
208✔
44

45
        copy->op_array.function_name = zend_string_copy(name);
208✔
46

47
        copy->op_array.scope = scope;
208✔
48

49
        if (flags & ZEND_ACC_PPP_MASK) {
208✔
50
                copy->op_array.fn_flags &= ~ZEND_ACC_PPP_MASK;
192✔
51
                copy->op_array.fn_flags |= flags & ZEND_ACC_PPP_MASK;
192✔
52
        } else {
53
                copy->op_array.fn_flags |= ZEND_ACC_PUBLIC;
16✔
54
        }
55

56
        if (flags & ZEND_ACC_STATIC) {
208✔
57
                copy->op_array.fn_flags |= ZEND_ACC_STATIC;
16✔
58
        }
59

60
        if (copy->op_array.static_variables) {
208✔
61
                copy->op_array.static_variables = zend_array_dup(copy->op_array.static_variables);
16✔
62

63
#if PHP_VERSION_ID >= 80200
64
                ZEND_MAP_PTR_INIT(
8✔
65
                        copy->op_array.static_variables_ptr, copy->op_array.static_variables);
66
#else
67
                ZEND_MAP_PTR_INIT(
8✔
68
                        copy->op_array.static_variables_ptr, &copy->op_array.static_variables);
69
#endif
70
        } else {
71
                ZEND_MAP_PTR_INIT(copy->op_array.static_variables_ptr, NULL);
192✔
72
        }
73

74
        if (copy->op_array.refcount) {
208✔
75
                (*copy->op_array.refcount)++;
208✔
76
        }
77
        copy->op_array.fn_flags |= ZEND_ACC_UOPZ;
208✔
78

79
        return copy;
208✔
80
} /* }}} */
81

82
zend_bool uopz_add_function(zend_class_entry *clazz, zend_string *name, zval *closure, zend_long flags, zend_bool all) { /* {{{ */
240✔
83
        HashTable *table = clazz ? &clazz->function_table : CG(function_table);
240✔
84
        zend_string *key;
120✔
85
        zend_function *function = NULL;
240✔
86

87
        if (clazz && clazz->ce_flags & ZEND_ACC_IMMUTABLE) {
240✔
88
                uopz_exception(
×
89
                        "cannot add method %s::%s to immutable class, use uopz_set_return instead",
90
                        ZSTR_VAL(clazz->name),
91
                        ZSTR_VAL(name));
92
                return 0;
×
93
        }
94

95
        key = zend_string_tolower(name);
240✔
96
        key = zend_new_interned_string(key);
240✔
97

98
        if (zend_hash_exists(table, key)) {
240✔
99
                if (clazz) {
32✔
100
                        uopz_exception(
16✔
101
                                "will not replace existing method %s::%s, use uopz_set_return instead",
102
                                ZSTR_VAL(clazz->name),
103
                                ZSTR_VAL(name));
104
                } else {
105
                        uopz_exception(
16✔
106
                                "will not replace existing function %s, use uopz_set_return instead",
107
                                ZSTR_VAL(name));
108
                }
109
                zend_string_release(key);
32✔
110
                return 0;
32✔
111
        }
112

113
        function = uopz_copy_function(clazz, name, Z_OBJ_P(closure), flags);
208✔
114
        zend_hash_update_ptr(table, key, (void*) function);
208✔
115

116
        if (clazz) {
208✔
117
                if (all) {
192✔
118
                        zend_class_entry *next;
96✔
119

120
                        ZEND_HASH_FOREACH_PTR(CG(class_table), next) {
25,488✔
121
                                if (next->parent == clazz) {
25,296✔
122
                                        if (zend_hash_exists(&next->function_table, key)) {
×
123
                                                continue;
×
124
                                        }
125
                                        uopz_add_function(next, name, closure, flags, all);
×
126
                                }
127
                        } ZEND_HASH_FOREACH_END();
128
                }
129

130
                uopz_handle_magic(clazz, name, function);
192✔
131
        }
132

133
        zend_string_release(key);
208✔
134

135
        return 1;
104✔
136
} /* }}} */
137

138
zend_bool uopz_del_function(zend_class_entry *clazz, zend_string *name, zend_bool all) { /* {{{ */
80✔
139
        HashTable *table = clazz ? &clazz->function_table : CG(function_table);
80✔
140
        zend_string *key = zend_string_tolower(name);
80✔
141
        zend_function *function = zend_hash_find_ptr(table, key);
80✔
142
        
143
        if (!function) {
64✔
144
                if (clazz) {
32✔
145
                        uopz_exception(
16✔
146
                                "cannot delete method %s::%s, it does not exist",
147
                                ZSTR_VAL(clazz->name),
148
                                ZSTR_VAL(name));
149
                } else {
150
                        uopz_exception(
16✔
151
                                "cannot delete function %s, it does not exist",
152
                                ZSTR_VAL(name));
153
                }
154
                zend_string_release(key);
32✔
155
                return 0;
32✔
156
        }
157
        
158
        if (!(function->common.fn_flags & ZEND_ACC_UOPZ)) {
48✔
159
                if (clazz) {
32✔
160
                        uopz_exception(
16✔
161
                                "cannot delete method %s::%s, it was not added by uopz",
162
                                ZSTR_VAL(clazz->name),
163
                                ZSTR_VAL(name));
164
                } else {
165
                        uopz_exception(
16✔
166
                                "cannot delete function %s, it was not added by uopz",
167
                                ZSTR_VAL(name));
168
                }
169
                zend_string_release(key);
32✔
170
                return 0;
32✔
171
        }
172
        
173
        if (clazz && all) {
16✔
174
                zend_class_entry *next;
8✔
175

176
                ZEND_HASH_FOREACH_PTR(CG(class_table), next) {
2,124✔
177
                        if (next->parent == clazz) {
2,108✔
178
                                if (!zend_hash_exists(&next->function_table, key)) {
×
179
                                        continue;
×
180
                                }
181
                                uopz_del_function(next, name, all);
×
182
                        }
183
                } ZEND_HASH_FOREACH_END();
184
        }
185

186
        zend_hash_del(table, key);
16✔
187
        zend_string_release(key);
16✔
188

189
        return 1;
8✔
190
} /* }}} */
191

192
/* {{{ */
193
void uopz_flags(zend_class_entry *clazz, zend_string *name, zend_long flags, zval *return_value) {
144✔
194
        HashTable *table = clazz ? &clazz->function_table : CG(function_table);
144✔
195
        zend_long current = 0;
144✔
196

197
        if (clazz && (!name || !ZSTR_LEN(name))) {
144✔
198
                if (flags == ZEND_LONG_MAX) {
64✔
199
                        RETURN_LONG(clazz->ce_flags);
16✔
200
                }
201

202
                if (flags & ZEND_ACC_PPP_MASK) {
48✔
203
                        uopz_exception(
16✔
204
                                "attempt to set public, private or protected on class entry %s, not allowed",
205
                                ZSTR_VAL(clazz->name));
206
                        return;
16✔
207
                }
208

209
                if (flags & ZEND_ACC_STATIC) {
32✔
210
                        uopz_exception(
16✔
211
                                "attempt to set static on class entry %s, not allowed",
212
                                ZSTR_VAL(clazz->name));
213
                        return;
16✔
214
                }
215

216
                if (clazz->ce_flags & ZEND_ACC_IMMUTABLE) {
16✔
217
                        uopz_exception(
×
218
                                "attempt to set flags of immutable class entry %s, not allowed",
219
                                ZSTR_VAL(clazz->name));
220
                        return;
×
221
                }
222

223
                current = clazz->ce_flags;
16✔
224
                clazz->ce_flags = flags;
16✔
225
                if (current & ZEND_ACC_LINKED) {
16✔
226
                        clazz->ce_flags |= ZEND_ACC_LINKED;
16✔
227
                }
228
                RETURN_LONG(current);
16✔
229
        }
230

231
        zend_function *function = uopz_find_function(table, name);
80✔
232
        if (!function) {
80✔
233
                if (clazz) {
48✔
234
                        uopz_exception(
16✔
235
                                "failed to set or get flags of method %s::%s, it does not exist",
236
                                ZSTR_VAL(clazz->name), ZSTR_VAL(name));
237
                } else {
238
                        uopz_exception(
32✔
239
                                "failed to set or get flags of function %s, it does not exist",
240
                                ZSTR_VAL(name));
241
                }
242
                return;
48✔
243
        }
244

245
        if (flags == ZEND_LONG_MAX) {
32✔
246
                RETURN_LONG(function->common.fn_flags);
16✔
247
        }
248

249
        current = function->common.fn_flags;
16✔
250
        if (flags) {
16✔
251
                if (function->common.fn_flags & ZEND_ACC_IMMUTABLE) {
16✔
252
                        uopz_exception(
×
253
                                "attempt to set flags of immutable function entry %s, not allowed",
254
                                ZSTR_VAL(name));
255
                        return;
×
256
                }
257

258
                /* Only allow changing a whitelist of flags, don't allow modifying internal flags. */
259
                uint32_t allowed_flags = ZEND_ACC_PPP_MASK | ZEND_ACC_STATIC | ZEND_ACC_FINAL;
16✔
260
                function->common.fn_flags =
16✔
261
                        (function->common.fn_flags & ~allowed_flags) | (flags & allowed_flags);
16✔
262
        }
263
        RETURN_LONG(current);
16✔
264
} /* }}} */
265

266
zend_bool uopz_set_static(zend_class_entry *clazz, zend_string *function, zval *statics) { /* {{{ */
176✔
267
        zend_function *entry;
88✔
268
        zend_string *k = NULL;
176✔
269
        zval *v = NULL;
176✔
270

271
        if (clazz) {
176✔
272
                entry = uopz_find_function(&clazz->function_table, function);
128✔
273
                if (!entry) {
128✔
274
                        uopz_exception(
16✔
275
                                "failed to set statics in method %s::%s, it does not exist",
276
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
277
                        return 0;
16✔
278
                }
279
        } else {
280
                entry = uopz_find_function(CG(function_table), function);
48✔
281
                if (!entry) {
48✔
282
                        uopz_exception(
16✔
283
                                "failed to set statics in function %s, it does not exist",
284
                                ZSTR_VAL(function));
285
                        return 0;
16✔
286
                }
287
        }
288

289
        if (entry->type != ZEND_USER_FUNCTION) {
144✔
290
                if (clazz) {
32✔
291
                        uopz_exception(
16✔
292
                                "failed to set statics in internal method %s::%s",
293
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
294
                } else {
295
                        uopz_exception(
16✔
296
                                "failed to set statics in internal function %s",
297
                                ZSTR_VAL(function));
298
                }
299

300
                return 0;
32✔
301
        }
302

303
        if (!entry->op_array.static_variables) {
112✔
304
                if (clazz) {
32✔
305
                        uopz_exception(
16✔
306
                                "failed to set statics in method %s::%s, no statics declared",
307
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
308
                } else {
309
                        uopz_exception(
16✔
310
                                "failed to set statics in function %s, no statics declared",
311
                                ZSTR_VAL(function));
312
                }
313

314
                return 0;
32✔
315
        }
316

317
        HashTable *variables = ZEND_MAP_PTR_GET(entry->op_array.static_variables_ptr);
80✔
318

319
        if (!variables) {
80✔
320
                variables = zend_array_dup(entry->op_array.static_variables);
14✔
321

322
                ZEND_MAP_PTR_SET(entry->op_array.static_variables_ptr, variables);
14✔
323
        }
324

325
        ZEND_HASH_FOREACH_STR_KEY_VAL(variables, k, v) {
160✔
326
                zval *y;
40✔
327

328
                if (Z_REFCOUNTED_P(v)) {
80✔
329
                        zval_ptr_dtor(v);
48✔
330
                }
331

332
                ZEND_ASSERT(Z_TYPE_P(statics) == IS_ARRAY);
80✔
333
                if (!(y = zend_hash_find(Z_ARRVAL_P(statics), k))) {
80✔
334
                        ZVAL_NULL(v);
16✔
335
                        
336
                        continue;
16✔
337
                }
338

339
                ZVAL_COPY(v, y);
72✔
340
        } ZEND_HASH_FOREACH_END();
341

342
        return 1;
40✔
343
} /* }}} */
344

345
zend_bool uopz_get_static(zend_class_entry *clazz, zend_string *function, zval *return_value) { /* {{{ */
192✔
346
        zend_function *entry;
96✔
347

348
        if (clazz) {
192✔
349
                entry = uopz_find_function(&clazz->function_table, function);
144✔
350
                if (!entry) {
144✔
351
                        uopz_exception(
16✔
352
                                "failed to get statics from method %s::%s, it does not exist",
353
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
354
                        return 0;
16✔
355
                }
356
        } else {
357
                entry = uopz_find_function(CG(function_table), function);
48✔
358
                if (!entry) {
48✔
359
                        uopz_exception(
16✔
360
                                "failed to get statics from function %s, it does not exist",
361
                                ZSTR_VAL(function));
362
                        return 0;
16✔
363
                }
364
        }
365

366
        if (entry->type != ZEND_USER_FUNCTION) {
160✔
367
                if (clazz) {
32✔
368
                        uopz_exception(
16✔
369
                                "failed to get statics from internal method %s::%s",
370
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
371
                } else {
372
                        uopz_exception(
16✔
373
                                "failed to get statics from internal function %s",
374
                                ZSTR_VAL(function));
375
                }
376

377
                return 0;
32✔
378
        }
379

380
        if (!entry->op_array.static_variables) {
128✔
381
                if (clazz) {
32✔
382
                        uopz_exception(
16✔
383
                                "failed to set statics in method %s::%s, no statics declared",
384
                                ZSTR_VAL(clazz->name), ZSTR_VAL(function));
385
                } else {
386
                        uopz_exception(
16✔
387
                                "failed to set statics in function %s, no statics declared",
388
                                ZSTR_VAL(function));
389
                }
390

391
                return 0;
32✔
392
        }
393

394
        HashTable *variables = ZEND_MAP_PTR_GET(entry->op_array.static_variables_ptr);
96✔
395

396
        if (!variables) {
96✔
397
                variables = zend_array_dup(entry->op_array.static_variables);
28✔
398
                ZEND_MAP_PTR_SET(entry->op_array.static_variables_ptr, variables);
28✔
399
        }
400

401
        zval *val;
48✔
402
        ZEND_HASH_FOREACH_VAL(variables, val) {
224✔
403
                if (zval_update_constant_ex(val, entry->common.scope) != SUCCESS) {
128✔
404
                        return false;
405
                }
406
        } ZEND_HASH_FOREACH_END();
407

408
        ZVAL_ARR(return_value, zend_array_dup(variables));
96✔
409
        return 1;
96✔
410
} /* }}} */
411

412
#ifndef ZEND_EXIT
413
void ZEND_FASTCALL uopz_exit_function(INTERNAL_FUNCTION_PARAMETERS) { /* {{{ */
414
        zend_string *str = NULL;
415
        zend_long code = 0;
416

417
        ZEND_PARSE_PARAMETERS_START(0, 1)
418
                Z_PARAM_OPTIONAL
419
                Z_PARAM_STR_OR_LONG(str, code)
420
        ZEND_PARSE_PARAMETERS_END();
421

422
    if (UOPZ(exit)) {
423
        zif_exit(INTERNAL_FUNCTION_PARAM_PASSTHRU);
424
    }
425
} /* }}} */
426
#endif
427

428
#endif        /* UOPZ_FUNCTION */
429

430
/*
431
 * Local variables:
432
 * tab-width: 4
433
 * c-basic-offset: 4
434
 * End:
435
 * vim600: noet sw=4 ts=4 fdm=marker
436
 * vim<600: noet sw=4 ts=4
437
 */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc