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

krakjoe / parallel / 20315085803

17 Dec 2025 07:39PM UTC coverage: 96.75% (-0.07%) from 96.815%
20315085803

push

github

web-flow
Cleanup code formatting and docs (#357)

* add comment about OPcache

* cleanup code formatting

2672 of 2765 new or added lines in 27 files covered. (96.64%)

1 existing line in 1 file now uncovered.

2798 of 2892 relevant lines covered (96.75%)

6639.73 hits per line

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

94.67
/src/check.c
1
/*
2
  +----------------------------------------------------------------------+
3
  | parallel                                                             |
4
  +----------------------------------------------------------------------+
5
  | Copyright (c) Joe Watkins 2019-2024                                  |
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: krakjoe                                                      |
16
  +----------------------------------------------------------------------+
17
 */
18
#ifndef HAVE_PARALLEL_CHECK
19
#define HAVE_PARALLEL_CHECK
20

21
#include "parallel.h"
22

23
TSRM_TLS struct {
24
        HashTable tasks;
25
        HashTable functions;
26
        HashTable types;
27
        HashTable classes;
28
} php_parallel_check_globals;
29

30
#ifndef ZEND_TYPE_IS_COMPLEX
31
#define ZEND_TYPE_IS_COMPLEX ZEND_TYPE_HAS_CLASS
32
#endif
33

34
#define PCG(e) php_parallel_check_globals.e
35

36
typedef struct _php_parallel_check_task_t {
37
        zend_function *function;
38
        bool           returns;
39
} php_parallel_check_task_t;
40

41
typedef struct _php_parallel_check_function_t {
42
        zend_function *function;
43
        zend_uchar     instruction;
44
        bool           valid;
45
} php_parallel_check_function_t;
46

47
typedef struct _php_parallel_check_type_t {
48
        bool valid;
49
} php_parallel_check_type_t;
50

51
typedef enum {
52
        PHP_PARALLEL_CHECK_CLASS_UNKNOWN,
53
        PHP_PARALLEL_CHECK_CLASS_VALID,
54
        PHP_PARALLEL_CHECK_CLASS_INVALID,
55
        PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY
56
} php_parallel_check_class_result_t;
57

58
typedef struct _php_parallel_check_class_t {
59
        php_parallel_check_class_result_t result;
60
} php_parallel_check_class_t;
61

62
static zend_always_inline const char *php_parallel_check_opcode_name(zend_uchar opcode)
162✔
63
{ /* {{{ */
64
        switch (opcode) {
162✔
65
        case ZEND_DECLARE_CLASS:
66
        case ZEND_DECLARE_CLASS_DELAYED:
67
                return "class";
68

69
        case ZEND_DECLARE_ANON_CLASS:
36✔
70
                return "new class";
36✔
71

72
        case ZEND_YIELD:
36✔
73
        case ZEND_YIELD_FROM:
74
                return "yield";
36✔
75

76
        case ZEND_DECLARE_FUNCTION:
36✔
77
                return "function";
36✔
78

79
        default:
80
                return "class";
81
        }
82
} /* }}} */
83

84
static zend_always_inline bool php_parallel_check_type(zend_type type)
294✔
85
{ /* {{{ */
86
        zend_string *name;
294✔
87
        zend_type   *single;
294✔
88
        zend_class_entry *class;
294✔
89
        php_parallel_check_type_t check, *checked;
294✔
90

91
        if (ZEND_TYPE_HAS_LIST(type)) {
294✔
92
                ZEND_TYPE_FOREACH(type, single)
36✔
93
                {
94
                        if (ZEND_TYPE_HAS_NAME(*single)) {
72✔
95
                                name = ZEND_TYPE_NAME(*single);
72✔
96

97
                                if ((checked = zend_hash_find_ptr(&PCG(types), name))) {
72✔
NEW
98
                                        if (!checked->valid) {
×
99
                                                return 0;
100
                                        }
NEW
101
                                        continue;
×
102
                                }
103

104
                                class = zend_lookup_class(ZEND_TYPE_NAME(*single));
72✔
105

106
                                if (!class) {
72✔
107
                                        return 0;
108
                                }
109

110
                                memset(&check, 0, sizeof(php_parallel_check_type_t));
54✔
111

112
                                if (class == zend_ce_closure || class == php_parallel_channel_ce ||
54✔
113
                                    instanceof_function(class, php_parallel_sync_ce) || !class->create_object) {
108✔
114

115
                                        check.valid = 1;
54✔
116
                                }
117

118
                                zend_hash_add_mem(&PCG(types), name, &check, sizeof(php_parallel_check_type_t));
54✔
119

120
                                if (!check.valid) {
54✔
121
                                        return 0;
122
                                }
123
                        }
124
                }
125
                ZEND_TYPE_FOREACH_END();
54✔
126

127
                return 1;
128
        }
129

130
        name = ZEND_TYPE_NAME(type);
258✔
131

132
        if ((checked = zend_hash_find_ptr(&PCG(types), name))) {
348✔
133
                return checked->valid;
90✔
134
        }
135

136
        class = zend_lookup_class(name);
168✔
137

138
        if (!class) {
168✔
139
                return 0;
140
        }
141

142
        memset(&check, 0, sizeof(php_parallel_check_type_t));
162✔
143

144
        if (class == zend_ce_closure || class == php_parallel_channel_ce ||
162✔
145
            instanceof_function(class, php_parallel_sync_ce) || !class->create_object) {
216✔
146

147
                check.valid = 1;
144✔
148
        }
149

150
        zend_hash_add_mem(&PCG(types), name, &check, sizeof(php_parallel_check_type_t));
162✔
151

152
        return check.valid;
162✔
153
} /* }}} */
154

155
static zend_always_inline bool php_parallel_check_arginfo(const zend_function *function)
2,148✔
156
{ /* {{{ */
157
        zend_arg_info *it, *end;
2,148✔
158
        int            argc = 1;
2,148✔
159

160
        if (!function->op_array.arg_info) {
2,148✔
161
                return 1;
162
        }
163

164
        if (function->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) {
720✔
165
                it = function->op_array.arg_info - 1;
90✔
166
                if (ZEND_TYPE_IS_SET(it->type) && ZEND_TYPE_IS_COMPLEX(it->type)) {
90✔
167
                        if (!php_parallel_check_type(it->type)) {
72✔
168
                                php_parallel_exception_ex(php_parallel_runtime_error_illegal_return_ce, "illegal return (%s) from task",
18✔
169
                                                          ZSTR_VAL(ZEND_TYPE_NAME(it->type)));
170
                                return 0;
18✔
171
                        }
172
                }
173

174
                if (function->common.fn_flags & ZEND_ACC_RETURN_REFERENCE) {
72✔
175
                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_return_ce,
18✔
176
                                                  "illegal return (reference) from task");
177
                        return 0;
18✔
178
                }
179
        }
180

181
        it = function->op_array.arg_info;
684✔
182
        end = it + function->op_array.num_args;
684✔
183

184
        if (function->common.fn_flags & ZEND_ACC_VARIADIC) {
684✔
185
                end++;
54✔
186
        }
187

188
        while (it < end) {
1,362✔
189
                if (ZEND_TYPE_IS_SET(it->type) && ZEND_TYPE_IS_COMPLEX(it->type)) {
810✔
190
                        if (!php_parallel_check_type(it->type)) {
516✔
191
                                php_parallel_exception_ex(php_parallel_runtime_error_illegal_parameter_ce,
78✔
192
                                                          "illegal parameter (%s) accepted by task at argument %d",
193
                                                          ZSTR_VAL(ZEND_TYPE_NAME(it->type)), argc);
194
                                return 0;
78✔
195
                        }
196
                }
197

198
                if (ZEND_ARG_SEND_MODE(it)) {
732✔
199
                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_parameter_ce,
54✔
200
                                                  "illegal parameter (reference) accepted by task at argument %d", argc);
201
                        return 0;
54✔
202
                }
203
                it++;
678✔
204
                argc++;
678✔
205
        }
206

207
        return 1;
208
} /* }}} */
209

210
static zend_always_inline bool php_parallel_check_statics(const zend_function *function, zend_string **errn,
2,877✔
211
                                                          zval **errz)
212
{ /* {{{ */
213
        HashTable   *statics;
2,877✔
214
        zval        *value, *error;
2,877✔
215
        zend_string *name;
2,877✔
216

217
        if (!function->op_array.static_variables) {
2,877✔
218
                return 1;
219
        }
220

221
        statics = ZEND_MAP_PTR_GET(function->op_array.static_variables_ptr);
723✔
222
        if (!statics) {
723✔
223
                statics = function->op_array.static_variables;
134✔
224
        }
225

226
        ZEND_HASH_FOREACH_STR_KEY_VAL(statics, name, value)
1,446✔
227
        {
228
                if (!PARALLEL_ZVAL_CHECK(value, &error)) {
741✔
229
                        if (errn) {
18✔
230
                                *errn = name;
18✔
231
                        }
232
                        if (errz) {
18✔
233
                                *errz = error;
18✔
234
                        }
235
                        return 0;
236
                }
237
        }
238
        ZEND_HASH_FOREACH_END();
239

240
        return 1;
241
} /* }}} */
242

243
static zend_always_inline bool php_parallel_check_argv(zval *args, uint32_t *argc, zval **error)
972✔
244
{ /* {{{ */
245
        zval *arg;
972✔
246

247
        if (*argc == 0) {
972✔
248
                *argc = 1;
972✔
249
        }
250

251
        ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(args), arg)
1,860✔
252
        {
253
                if (!PARALLEL_ZVAL_CHECK(arg, error)) {
990✔
254
                        return 0;
255
                }
256

257
                (*argc)++;
888✔
258
        }
259
        ZEND_HASH_FOREACH_END();
260

261
        return 1;
262
} /* }}} */
263

264
static zend_always_inline bool php_parallel_check_use(zend_execute_data *execute_data, const zend_function *function,
168✔
265
                                                      zend_op *bind)
266
{ /* {{{ */
267
        zend_op *opline, *end;
168✔
268

269
        if (EX(func)->type != ZEND_USER_FUNCTION) {
168✔
270
                return 0;
271
        }
272

273
        opline = EX(func)->op_array.opcodes;
168✔
274
        end = opline + EX(func)->op_array.last;
168✔
275

276
        while (opline < end) {
4,266✔
277
                if ((opline->opcode == ZEND_BIND_LEXICAL) && (opline->extended_value & ZEND_BIND_REF)) {
4,116✔
278
                        if (zend_string_equals(zend_get_compiled_variable_name((zend_op_array *)function, bind->op1.var),
18✔
279
                                               zend_get_compiled_variable_name((zend_op_array *)EX(func), opline->op2.var))) {
18✔
280
                                return 1;
281
                        }
282
                }
283
                opline++;
4,098✔
284
        }
285

286
        return 0;
287
} /* }}} */
288

289
bool php_parallel_check_task(php_parallel_runtime_t *runtime, zend_execute_data *execute_data,
2,550✔
290
                             const zend_function *function, zval *argv, bool *returns)
291
{ /* {{{ */
292
        php_parallel_check_task_t check, *checked;
2,550✔
293
        zend_op                  *it, *end;
2,550✔
294

295
        if (function->type != ZEND_USER_FUNCTION) {
2,550✔
296
                php_parallel_exception_ex(php_parallel_runtime_error_illegal_function_ce, "illegal function type (internal)");
18✔
297
                return 0;
18✔
298
        }
299

300
        if (argv && Z_TYPE_P(argv) == IS_ARRAY) {
2,532✔
301
                uint32_t errat = 0;
972✔
302
                zval    *errarg;
972✔
303

304
                if (!php_parallel_check_argv(argv, &errat, &errarg)) {
1,944✔
305
                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_parameter_ce,
102✔
306
                                                  "illegal parameter (%s) passed to task at argument %d",
307
                                                  Z_TYPE_P(errarg) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(errarg)->name)
308
                                                                                : zend_get_type_by_const(Z_TYPE_P(errarg)),
309
                                                  errat);
310
                        return 0;
102✔
311
                }
312
        }
313

314
        if ((checked = zend_hash_index_find_ptr(&PCG(tasks), (zend_ulong)function->op_array.opcodes))) {
2,712✔
315
                *returns = checked->returns;
282✔
316

317
                if (!*returns) {
282✔
318
                        if (EX(opline)->result_type != IS_UNUSED) {
72✔
319
                                *returns = 1;
24✔
320
                        }
321
                }
322

323
                return 1;
282✔
324
        }
325

326
        if (!php_parallel_check_arginfo(function)) {
2,316✔
327
                return 0;
168✔
328
        }
329

330
        {
331
                zend_string *errn;
1,980✔
332
                zval        *errv;
1,980✔
333
                if (!php_parallel_check_statics(function, &errn, &errv)) {
2,148✔
334
                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_variable_ce,
18✔
335
                                                  "illegal variable (%s) named %s in static scope of function",
336
                                                  Z_TYPE_P(errv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(errv)->name)
337
                                                                              : zend_get_type_by_const(Z_TYPE_P(errv)),
338
                                                  ZSTR_VAL(errn));
339
                        return 0;
18✔
340
                }
341
        }
342

343
        memset(&check, 0, sizeof(php_parallel_check_task_t));
1,962✔
344

345
        it = function->op_array.opcodes;
1,962✔
346
        end = it + function->op_array.last;
1,962✔
347

348
        while (it < end) {
13,329✔
349
                switch (it->opcode) {
11,619✔
350
                case ZEND_DECLARE_LAMBDA_FUNCTION: {
267✔
351
                        zend_string   *key;
267✔
352
                        zend_function *dependency, *errf;
267✔
353
                        zend_uchar     erro;
267✔
354

355
                        PARALLEL_COPY_OPLINE_TO_FUNCTION(function, it, &key, &dependency);
267✔
356

357
                        if (!php_parallel_check_function(dependency, &errf, &erro)) {
267✔
358
                                php_parallel_exception_ex(php_parallel_runtime_error_illegal_instruction_ce,
54✔
359
                                                          "illegal instruction (%s) in closure on line %d of task",
360
                                                          php_parallel_check_opcode_name(erro),
361
                                                          errf->op_array.line_start - function->op_array.line_start);
362
                                return 0;
54✔
363
                        }
364

365
                        {
366
                                zend_string *errn;
213✔
367
                                zval        *errv;
213✔
368
                                if (!php_parallel_check_statics(dependency, &errn, &errv)) {
354✔
NEW
369
                                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_variable_ce,
×
370
                                                                  "illegal variable (%s) named %s in static scope of dependency",
371
                                                                  Z_TYPE_P(errv) == IS_OBJECT ? ZSTR_VAL(Z_OBJCE_P(errv)->name)
372
                                                                                              : zend_get_type_by_const(Z_TYPE_P(errv)),
373
                                                                  ZSTR_VAL(errn));
NEW
374
                                        return 0;
×
375
                                }
376
                        }
377
                } break;
378

379
                case ZEND_DECLARE_FUNCTION:
108✔
380
                case ZEND_DECLARE_CLASS:
381
                case ZEND_DECLARE_ANON_CLASS:
382
                case ZEND_DECLARE_CLASS_DELAYED:
383
                case ZEND_YIELD:
384
                case ZEND_YIELD_FROM:
385
                        php_parallel_exception_ex(
108✔
386
                            php_parallel_runtime_error_illegal_instruction_ce, "illegal instruction (%s) on line %d of task",
387
                            php_parallel_check_opcode_name(it->opcode), it->lineno - function->op_array.line_start);
388
                        return 0;
108✔
389

390
                case ZEND_BIND_STATIC:
391
                        if (php_parallel_check_use(execute_data, function, it)) {
336✔
392
                                php_parallel_exception_ex(php_parallel_runtime_error_illegal_instruction_ce,
18✔
393
                                                          "illegal instruction (lexical reference) in task");
394
                                return 0;
18✔
395
                        }
396
                        break;
397

398
                case ZEND_THROW:
1,966✔
399
                case ZEND_RETURN:
400
                        if (!*returns && it->extended_value != -1) {
1,966✔
401
                                if (EX(opline)->result_type == IS_UNUSED) {
636✔
402
                                        php_parallel_exception_ex(php_parallel_runtime_error_illegal_return_ce,
72✔
403
                                                                  "return on line %d of task ignored by caller, "
404
                                                                  "caller must retain reference to Future",
405
                                                                  it->lineno - function->op_array.line_start);
406
                                        return 0;
72✔
407
                                }
408
                                *returns = 1;
564✔
409
                        }
410
                        break;
411
                }
412
                it++;
11,367✔
413
        }
414

415
        check.returns = *returns;
1,710✔
416

417
        if (!*returns) {
1,710✔
418
                if (EX(opline)->result_type != IS_UNUSED) {
1,146✔
419
                        *returns = 1;
324✔
420
                }
421
        }
422

423
        zend_hash_index_add_mem(&PCG(tasks), (zend_ulong)function->op_array.opcodes, &check,
1,710✔
424
                                sizeof(php_parallel_check_task_t));
425

426
        return 1;
1,710✔
427
} /* }}} */
428

429
bool php_parallel_check_function(const zend_function *function, zend_function **errf, zend_uchar *erro)
1,005✔
430
{ /* {{{ */
431
        php_parallel_check_function_t check, *checked;
1,005✔
432
        zend_op                      *it, *end;
1,005✔
433

434
        if ((checked = zend_hash_index_find_ptr(&PCG(functions), (zend_ulong)function->op_array.opcodes))) {
1,365✔
435
                check = *checked;
360✔
436

437
                goto _php_parallel_checked_function_return;
360✔
438
        }
439

440
        memset(&check, 0, sizeof(php_parallel_check_function_t));
645✔
441

442
        it = function->op_array.opcodes;
645✔
443
        end = it + function->op_array.last;
645✔
444

445
        while (it < end) {
3,022✔
446
                switch (it->opcode) {
2,503✔
447
                case ZEND_DECLARE_FUNCTION:
90✔
448
                case ZEND_DECLARE_CLASS:
449
                case ZEND_DECLARE_CLASS_DELAYED:
450
                case ZEND_DECLARE_ANON_CLASS:
451
                        check.function = (zend_function *)function;
90✔
452
                        check.instruction = it->opcode;
90✔
453

454
                        goto _php_parallel_checked_function_add;
90✔
455

456
                case ZEND_DECLARE_LAMBDA_FUNCTION: {
54✔
457
                        zend_string   *key;
54✔
458
                        zend_function *dependency;
54✔
459

460
                        PARALLEL_COPY_OPLINE_TO_FUNCTION(function, it, &key, &dependency);
54✔
461

462
                        if (!php_parallel_check_function(dependency, &check.function, &check.instruction)) {
54✔
463
                                goto _php_parallel_checked_function_add;
36✔
464
                        }
465
                } break;
466
                }
467
                it++;
2,377✔
468
        }
469

470
        check.valid = 1;
519✔
471

472
_php_parallel_checked_function_add:
645✔
473
        zend_hash_index_add_mem(&PCG(functions), (zend_ulong)function->op_array.opcodes, &check,
645✔
474
                                sizeof(php_parallel_check_function_t));
475

476
_php_parallel_checked_function_return:
1,005✔
477
        if (errf) {
1,005✔
478
                *errf = check.function;
321✔
479
        }
480

481
        if (erro) {
1,005✔
482
                *erro = check.instruction;
321✔
483
        }
484

485
        return check.valid;
1,005✔
486
} /* }}} */
487

488
static zend_always_inline bool php_parallel_check_closure(zend_closure_t *closure)
684✔
489
{ /* {{{ */
490
        return php_parallel_check_statics(&closure->func, NULL, NULL) &&
1,368✔
491
               php_parallel_check_function(&closure->func, NULL, NULL);
684✔
492
} /* }}} */
493

494
static php_parallel_check_class_result_t                    php_parallel_check_class(zend_class_entry *ce);
495

496
static zend_always_inline php_parallel_check_class_result_t php_parallel_check_class_inline(zend_class_entry *ce)
294✔
497
{ /* {{{ */
498
        zend_property_info        *info;
294✔
499
        php_parallel_check_class_t check, *checked = zend_hash_index_find_ptr(&PCG(classes), (zend_ulong)ce);
588✔
500

501
        if (checked) {
18✔
502
                return checked->result;
18✔
503
        }
504

505
        if (!ce) {
144✔
506
                return PHP_PARALLEL_CHECK_CLASS_INVALID;
507
        }
508

509
        memset(&check, 0, sizeof(php_parallel_check_class_t));
258✔
510

511
        if (ce == php_parallel_channel_ce || ce == zend_ce_closure) {
258✔
NEW
512
                check.result = PHP_PARALLEL_CHECK_CLASS_VALID;
×
513

NEW
514
                goto _php_parallel_checked_class;
×
515
        }
516

517
        if (ce->create_object) {
258✔
518
                check.result = PHP_PARALLEL_CHECK_CLASS_INVALID;
18✔
519

520
                goto _php_parallel_checked_class;
18✔
521
        }
522

523
        if (!ce->default_properties_count) {
240✔
524
                check.result = PHP_PARALLEL_CHECK_CLASS_VALID;
90✔
525

526
                goto _php_parallel_checked_class;
90✔
527
        }
528

529
        ZEND_HASH_FOREACH_PTR(&ce->properties_info, info)
240✔
530
        {
531
                zend_class_entry *next;
168✔
532

533
                if (!ZEND_TYPE_IS_SET(info->type)) {
168✔
534
                        check.result = PHP_PARALLEL_CHECK_CLASS_UNKNOWN;
42✔
535

536
                        goto _php_parallel_checked_class;
42✔
537
                }
538

539
                if (!ZEND_TYPE_IS_SET(info->type) || !ZEND_TYPE_IS_COMPLEX(info->type)) {
126✔
540
                        continue;
36✔
541
                }
542

543
                if (ZEND_TYPE_HAS_LIST(info->type)) {
90✔
544
                        zend_type *single;
36✔
545

546
                        ZEND_TYPE_FOREACH(info->type, single)
36✔
547
                        {
548
#ifdef ZEND_TYPE_HAS_CE
549
                                if (ZEND_TYPE_HAS_CE(*single)) {
15✔
550
                                        next = ZEND_TYPE_CE(*single);
551
                                } else
552
#endif
553
                                    if (ZEND_TYPE_HAS_NAME(*single)) {
90✔
554
                                        next = zend_lookup_class(ZEND_TYPE_NAME(*single));
90✔
555
                                } else {
NEW
556
                                        continue;
×
557
                                }
558

559
                                if (next == ce) {
90✔
NEW
560
                                        continue;
×
561
                                }
562

563
                                memset(&check, 0, sizeof(php_parallel_check_class_t));
90✔
564

565
                                switch (php_parallel_check_class(next)) {
90✔
566
                                case PHP_PARALLEL_CHECK_CLASS_INVALID:
18✔
567
                                case PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY:
568
                                        check.result = PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY;
18✔
569
                                        break;
18✔
570

571
                                case PHP_PARALLEL_CHECK_CLASS_UNKNOWN:
572
                                        check.result = PHP_PARALLEL_CHECK_CLASS_UNKNOWN;
573
                                        break;
574

575
                                case PHP_PARALLEL_CHECK_CLASS_VALID:
72✔
576
                                        check.result = PHP_PARALLEL_CHECK_CLASS_VALID;
72✔
577
                                        break;
72✔
578
                                }
579

580
                                if (check.result != PHP_PARALLEL_CHECK_CLASS_VALID) {
90✔
581
                                        zend_hash_index_add_mem(&PCG(classes), (zend_ulong)next, &check,
18✔
582
                                                                sizeof(php_parallel_check_class_t));
583

584
                                        check.result = PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY;
18✔
585

586
                                        zend_hash_index_add_mem(&PCG(classes), (zend_ulong)ce, &check, sizeof(php_parallel_check_class_t));
18✔
587

NEW
588
                                        return check.result;
×
589
                                }
590
                        }
591
                        ZEND_TYPE_FOREACH_END();
72✔
592

593
                } else {
594
#ifdef ZEND_TYPE_HAS_CE
595
                        if (ZEND_TYPE_HAS_CE(info->type)) {
9✔
596
                                next = ZEND_TYPE_CE(info->type);
9✔
597
                        } else
598
#endif
599
                        {
600
                                next = zend_lookup_class(ZEND_TYPE_NAME(info->type));
45✔
601
                        }
602

603
                        if (next == ce) {
54✔
NEW
604
                                continue;
×
605
                        }
606

607
                        switch (php_parallel_check_class(next)) {
54✔
608
                        case PHP_PARALLEL_CHECK_CLASS_INVALID:
18✔
609
                        case PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY:
610
                                check.result = PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY;
18✔
611
                                goto _php_parallel_checked_class;
18✔
612

NEW
613
                        case PHP_PARALLEL_CHECK_CLASS_UNKNOWN:
×
NEW
614
                                check.result = PHP_PARALLEL_CHECK_CLASS_UNKNOWN;
×
NEW
615
                                goto _php_parallel_checked_class;
×
616

617
                        case PHP_PARALLEL_CHECK_CLASS_VALID:
618
                                /* do nothing */
619
                                break;
620
                        }
621
                }
622
        }
623
        ZEND_HASH_FOREACH_END();
624

625
        check.result = PHP_PARALLEL_CHECK_CLASS_VALID;
72✔
626

627
_php_parallel_checked_class:
240✔
628
        checked = zend_hash_index_add_mem(&PCG(classes), (zend_ulong)ce, &check, sizeof(php_parallel_check_class_t));
240✔
629

630
        return checked->result;
240✔
631
} /* }}} */
632

633
static php_parallel_check_class_result_t php_parallel_check_class(zend_class_entry *ce)
144✔
634
{
635
        return php_parallel_check_class_inline(ce);
144✔
636
}
637

638
static zend_always_inline bool php_parallel_check_object(zend_object *object, zval **error)
552✔
639
{ /* {{{ */
640
        if (instanceof_function(object->ce, php_parallel_channel_ce) ||
552✔
641
            instanceof_function(object->ce, php_parallel_sync_ce)) {
750✔
642
                return 1;
643
        }
644

645
        if (object->ce->create_object) {
330✔
646
                return 0;
647
        }
648

649
        if ((object->properties == NULL) || (object->properties->nNumUsed == 0)) {
174✔
650
                switch (php_parallel_check_class_inline(object->ce)) {
300✔
651
                case PHP_PARALLEL_CHECK_CLASS_VALID:
652
                        /* whole graph is typed and allowed */
653
                        return 1;
654

655
                case PHP_PARALLEL_CHECK_CLASS_INVALID:
656
                        return 0;
657

658
                case PHP_PARALLEL_CHECK_CLASS_INVALID_PROPERTY:
659
                        /* type info for a property in graph is invalid, but may be undef/null */
660
                case PHP_PARALLEL_CHECK_CLASS_UNKNOWN:
661
                        /* not enough type info */
662
                        break;
663
                }
664
        }
665

666
        if (object->ce->default_properties_count) {
102✔
667
                zval *property = object->properties_table, *end = property + object->ce->default_properties_count;
78✔
668

669
                while (property < end) {
150✔
670
                        if ((Z_TYPE_P(property) == IS_OBJECT) && (Z_OBJ_P(property) == object)) {
96✔
671
                                property++;
18✔
672
                                continue;
18✔
673
                        }
674

675
                        if (!php_parallel_check_zval(property, error)) {
78✔
676
                                return 0;
677
                        }
678

679
                        property++;
54✔
680
                }
681
        }
682

683
        if (object->properties) {
78✔
684
                zval *property;
24✔
685

686
                ZEND_HASH_FOREACH_VAL(object->properties, property)
42✔
687
                {
688
                        if ((Z_TYPE_P(property) == IS_OBJECT) && (Z_OBJ_P(property) == object)) {
24✔
NEW
689
                                continue;
×
690
                        }
691

692
                        if (!php_parallel_check_zval(property, error)) {
24✔
693
                                return 0;
694
                        }
695
                }
696
                ZEND_HASH_FOREACH_END();
697
        }
698

699
        return 1;
700
} /* }}} */
701

702
static zend_always_inline bool php_parallel_check_resource(zval *zv)
18✔
703
{ /* {{{ */
704
        zend_resource *resource = Z_RES_P(zv);
18✔
705

706
        if (resource->type == php_file_le_stream() || resource->type == php_file_le_pstream()) {
18✔
707
                return 1;
18✔
708
        }
709

710
        return 0;
711
} /* }}} */
712

713
bool php_parallel_check_zval(zval *zv, zval **error)
4,047✔
714
{ /* {{{ */
715
        switch (Z_TYPE_P(zv)) {
4,047✔
716
        case IS_OBJECT:
717
                if (PARALLEL_ZVAL_CHECK_CLOSURE(zv)) {
1,236✔
718
                        if (!php_parallel_check_closure((zend_closure_t *)Z_OBJ_P(zv))) {
1,368✔
719
                                if (error) {
36✔
720
                                        *error = zv;
36✔
721
                                }
722
                                return 0;
36✔
723
                        }
724
                        return 1;
725
                } else if (php_parallel_check_object(Z_OBJ_P(zv), error)) {
600✔
726
                        return 1;
366✔
727
                }
728

729
                if (error) {
186✔
730
                        *error = zv;
186✔
731
                }
732
                return 0;
733

734
        case IS_ARRAY: {
486✔
735
                zval *el;
486✔
736

737
                ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(zv), el)
1,044✔
738
                {
739
                        if ((Z_TYPE_P(el) == IS_ARRAY) && (Z_ARRVAL_P(el) == Z_ARRVAL_P(zv))) {
558✔
NEW
740
                                continue;
×
741
                        }
742

743
                        if (!php_parallel_check_zval(el, error)) {
558✔
744
                                if (error) {
18✔
745
                                        *error = el;
18✔
746
                                }
747
                                return 0;
18✔
748
                        }
749
                }
750
                ZEND_HASH_FOREACH_END();
751
        } break;
752

753
        case IS_RESOURCE:
754
                if (php_parallel_check_resource(zv)) {
18✔
755
                        return 1;
18✔
756
                }
757

NEW
758
                if (error) {
×
NEW
759
                        *error = zv;
×
760
                }
761
                return 0;
762
        }
763
        return 1;
764
} /* }}} */
765

766
/* {{{ */
767
static void php_parallel_checked_dtor(zval *zv) { efree(Z_PTR_P(zv)); }
2,847✔
768

769
PHP_RINIT_FUNCTION(PARALLEL_CHECK)
5,352✔
770
{
771
        zend_hash_init(&PCG(tasks), 32, NULL, php_parallel_checked_dtor, 0);
5,352✔
772
        zend_hash_init(&PCG(functions), 32, NULL, php_parallel_checked_dtor, 0);
5,352✔
773
        zend_hash_init(&PCG(types), 32, NULL, php_parallel_checked_dtor, 0);
5,352✔
774
        zend_hash_init(&PCG(classes), 32, NULL, php_parallel_checked_dtor, 0);
5,352✔
775

776
        return SUCCESS;
5,352✔
777
}
778

779
PHP_RSHUTDOWN_FUNCTION(PARALLEL_CHECK)
5,352✔
780
{
781
        zend_hash_destroy(&PCG(classes));
5,352✔
782
        zend_hash_destroy(&PCG(types));
5,352✔
783
        zend_hash_destroy(&PCG(functions));
5,352✔
784
        zend_hash_destroy(&PCG(tasks));
5,352✔
785

786
        return SUCCESS;
5,352✔
787
}
788
/* }}} */
789
#endif
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