php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
zend_closures.c
Go to the documentation of this file.
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: Christian Seiler <chris_se@gmx.net> |
16 | Dmitry Stogov <dmitry@php.net> |
17 | Marcus Boerger <helly@php.net> |
18 +----------------------------------------------------------------------+
19*/
20
21#include "zend.h"
22#include "zend_API.h"
23#include "zend_closures.h"
24#include "zend_exceptions.h"
25#include "zend_interfaces.h"
26#include "zend_objects.h"
27#include "zend_objects_API.h"
28#include "zend_globals.h"
30
38
39/* non-static since it needs to be referenced */
41static zend_object_handlers closure_handlers;
42
43ZEND_METHOD(Closure, __invoke) /* {{{ */
44{
47 uint32_t num_args;
49
53
57
58 /* destruct the function also, then - we have allocated it in get_method */
59 zend_string_release_ex(func->internal_function.function_name, 0);
61
62 /* Set the func pointer to NULL. Prior to PHP 8.3, this was only done for debug builds,
63 * because debug builds check certain properties after the call and needed to know this
64 * had been freed.
65 * However, extensions can proxy zend_execute_internal, and it's a bit surprising to have
66 * an invalid func pointer sitting on there, so this was changed in PHP 8.3.
67 */
69}
70/* }}} */
71
72static bool zend_valid_closure_binding(
73 zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
74{
75 zend_function *func = &closure->func;
76 bool is_fake_closure = (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0;
77 if (newthis) {
78 if (func->common.fn_flags & ZEND_ACC_STATIC) {
79 zend_error(E_WARNING, "Cannot bind an instance to a static closure");
80 return 0;
81 }
82
83 if (is_fake_closure && func->common.scope &&
84 !instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
85 /* Binding incompatible $this to an internal method is not supported. */
86 zend_error(E_WARNING, "Cannot bind method %s::%s() to object of class %s",
87 ZSTR_VAL(func->common.scope->name),
88 ZSTR_VAL(func->common.function_name),
89 ZSTR_VAL(Z_OBJCE_P(newthis)->name));
90 return 0;
91 }
92 } else if (is_fake_closure && func->common.scope
93 && !(func->common.fn_flags & ZEND_ACC_STATIC)) {
94 zend_error(E_WARNING, "Cannot unbind $this of method");
95 return 0;
96 } else if (!is_fake_closure && !Z_ISUNDEF(closure->this_ptr)
97 && (func->common.fn_flags & ZEND_ACC_USES_THIS)) {
98 zend_error(E_WARNING, "Cannot unbind $this of closure using $this");
99 return 0;
100 }
101
102 if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) {
103 /* rebinding to internal class is not allowed */
104 zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s",
105 ZSTR_VAL(scope->name));
106 return 0;
107 }
108
109 if (is_fake_closure && scope != func->common.scope) {
110 if (func->common.scope == NULL) {
111 zend_error(E_WARNING, "Cannot rebind scope of closure created from function");
112 } else {
113 zend_error(E_WARNING, "Cannot rebind scope of closure created from method");
114 }
115 return 0;
116 }
117
118 return 1;
119}
120/* }}} */
121
122/* {{{ Call closure, binding to a given object with its class as the scope */
124{
125 zval *newthis, closure_result;
126 zend_closure *closure;
127 zend_fcall_info fci;
128 zend_fcall_info_cache fci_cache;
129 zend_object *newobj;
130 zend_class_entry *newclass;
131
132 fci.param_count = 0;
133 fci.params = NULL;
134
136 Z_PARAM_OBJECT(newthis)
139
140 closure = (zend_closure *) Z_OBJ_P(ZEND_THIS);
141
142 newobj = Z_OBJ_P(newthis);
143 newclass = newobj->ce;
144
145 if (!zend_valid_closure_binding(closure, newthis, newclass)) {
146 return;
147 }
148
149 fci_cache.called_scope = newclass;
150 fci_cache.object = fci.object = newobj;
151
152 fci.size = sizeof(fci);
153 ZVAL_OBJ(&fci.function_name, &closure->std);
154 ZVAL_UNDEF(&closure_result);
155 fci.retval = &closure_result;
156
157 if (closure->func.common.fn_flags & ZEND_ACC_GENERATOR) {
158 zval new_closure;
159 zend_create_closure(&new_closure, &closure->func, newclass, closure->called_scope, newthis);
160 closure = (zend_closure *) Z_OBJ(new_closure);
161 fci_cache.function_handler = &closure->func;
162
163 zend_call_function(&fci, &fci_cache);
164
165 /* copied upon generator creation */
166 GC_DELREF(&closure->std);
167 } else {
168 zend_closure *fake_closure;
169 zend_function *my_function;
170
171 fake_closure = emalloc(sizeof(zend_closure));
172 memset(&fake_closure->std, 0, sizeof(fake_closure->std));
173 fake_closure->std.gc.refcount = 1;
174 fake_closure->std.gc.u.type_info = GC_NULL;
175 ZVAL_UNDEF(&fake_closure->this_ptr);
176 fake_closure->called_scope = NULL;
177 my_function = &fake_closure->func;
178 if (ZEND_USER_CODE(closure->func.type)) {
179 memcpy(my_function, &closure->func, sizeof(zend_op_array));
180 } else {
181 memcpy(my_function, &closure->func, sizeof(zend_internal_function));
182 }
183 /* use scope of passed object */
184 my_function->common.scope = newclass;
185 if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
186 my_function->internal_function.handler = closure->orig_internal_handler;
187 }
188 fci_cache.function_handler = my_function;
189
190 /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
191 if (ZEND_USER_CODE(my_function->type)
192 && (closure->func.common.scope != newclass
193 || (closure->func.common.fn_flags & ZEND_ACC_HEAP_RT_CACHE))) {
194 void *ptr;
195
197 ptr = emalloc(my_function->op_array.cache_size);
198 ZEND_MAP_PTR_INIT(my_function->op_array.run_time_cache, ptr);
199 memset(ptr, 0, my_function->op_array.cache_size);
200 }
201
202 zend_call_function(&fci, &fci_cache);
203
204 if (ZEND_USER_CODE(my_function->type)) {
206 efree(ZEND_MAP_PTR(my_function->op_array.run_time_cache));
207 }
208 }
209 efree_size(fake_closure, sizeof(zend_closure));
210 }
211
212 if (Z_TYPE(closure_result) != IS_UNDEF) {
213 if (Z_ISREF(closure_result)) {
214 zend_unwrap_reference(&closure_result);
215 }
216 ZVAL_COPY_VALUE(return_value, &closure_result);
217 }
218}
219/* }}} */
220
221static void do_closure_bind(zval *return_value, zval *zclosure, zval *newthis, zend_object *scope_obj, zend_string *scope_str)
222{
223 zend_class_entry *ce, *called_scope;
224 zend_closure *closure = (zend_closure *) Z_OBJ_P(zclosure);
225
226 if (scope_obj) {
227 ce = scope_obj->ce;
228 } else if (scope_str) {
229 if (zend_string_equals(scope_str, ZSTR_KNOWN(ZEND_STR_STATIC))) {
230 ce = closure->func.common.scope;
231 } else if ((ce = zend_lookup_class(scope_str)) == NULL) {
232 zend_error(E_WARNING, "Class \"%s\" not found", ZSTR_VAL(scope_str));
233 RETURN_NULL();
234 }
235 } else {
236 ce = NULL;
237 }
238
239 if (!zend_valid_closure_binding(closure, newthis, ce)) {
240 return;
241 }
242
243 if (newthis) {
244 called_scope = Z_OBJCE_P(newthis);
245 } else {
246 called_scope = ce;
247 }
248
249 zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
250}
251
252/* {{{ Create a closure from another one and bind to another object and scope */
254{
255 zval *zclosure, *newthis;
256 zend_object *scope_obj = NULL;
257 zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
258
263 Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
265
266 do_closure_bind(return_value, zclosure, newthis, scope_obj, scope_str);
267}
268
269/* {{{ Create a closure from another one and bind to another object and scope */
271{
272 zval *newthis;
273 zend_object *scope_obj = NULL;
274 zend_string *scope_str = ZSTR_KNOWN(ZEND_STR_STATIC);
275
279 Z_PARAM_OBJ_OR_STR_OR_NULL(scope_obj, scope_str)
281
282 do_closure_bind(return_value, ZEND_THIS, newthis, scope_obj, scope_str);
283}
284
285static ZEND_NAMED_FUNCTION(zend_closure_call_magic) /* {{{ */ {
286 zend_fcall_info fci;
288 zval params[2];
289
290 memset(&fci, 0, sizeof(zend_fcall_info));
291 memset(&fcc, 0, sizeof(zend_fcall_info_cache));
292
293 fci.size = sizeof(zend_fcall_info);
294 fci.retval = return_value;
295
296 fcc.function_handler = (EX(func)->internal_function.fn_flags & ZEND_ACC_STATIC) ?
298 fci.named_params = NULL;
299 fci.params = params;
300 fci.param_count = 2;
301 ZVAL_STR(&fci.params[0], EX(func)->common.function_name);
304 zval *named_param_zval;
305 array_init_size(&fci.params[1], ZEND_NUM_ARGS() + zend_hash_num_elements(EX(extra_named_params)));
306 /* Avoid conversion from packed to mixed later. */
309 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(EX(extra_named_params), name, named_param_zval) {
310 Z_TRY_ADDREF_P(named_param_zval);
311 zend_hash_add_new(Z_ARRVAL(fci.params[1]), name, named_param_zval);
313 } else if (ZEND_NUM_ARGS()) {
316 } else {
317 ZVAL_EMPTY_ARRAY(&fci.params[1]);
318 }
319
320 fcc.object = fci.object = Z_OBJ_P(ZEND_THIS);
321 fcc.called_scope = zend_get_called_scope(EG(current_execute_data));
322
323 zend_call_function(&fci, &fcc);
324
325 zval_ptr_dtor(&fci.params[1]);
326}
327/* }}} */
328
329static zend_result zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) /* {{{ */ {
331 zend_function *mptr;
332 zval instance;
334
335 if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) {
336 return FAILURE;
337 }
338
339 mptr = fcc.function_handler;
341 /* For Closure::fromCallable([$closure, "__invoke"]) return $closure. */
342 if (fcc.object && fcc.object->ce == zend_ce_closure
343 && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) {
346 return SUCCESS;
347 }
348
349 if (!mptr->common.scope) {
350 return FAILURE;
351 }
352 if (mptr->common.fn_flags & ZEND_ACC_STATIC) {
353 if (!mptr->common.scope->__callstatic) {
354 return FAILURE;
355 }
356 } else {
357 if (!mptr->common.scope->__call) {
358 return FAILURE;
359 }
360 }
361
362 memset(&call, 0, sizeof(zend_internal_function));
365 call.handler = zend_closure_call_magic;
366 call.function_name = mptr->common.function_name;
367 call.scope = mptr->common.scope;
368 call.doc_comment = NULL;
369 call.attributes = mptr->common.attributes;
370
372 mptr = (zend_function *) &call;
373 }
374
375 if (fcc.object) {
376 ZVAL_OBJ(&instance, fcc.object);
377 zend_create_fake_closure(return_value, mptr, mptr->common.scope, fcc.called_scope, &instance);
378 } else {
380 }
381
382 if (&mptr->internal_function == &call) {
383 zend_string_release(mptr->common.function_name);
384 }
385
386 return SUCCESS;
387}
388/* }}} */
389
390/* {{{ Create a closure from a callable using the current scope. */
391ZEND_METHOD(Closure, fromCallable)
392{
393 zval *callable;
394 char *error = NULL;
395
397 Z_PARAM_ZVAL(callable)
399
400 if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) {
401 /* It's already a closure */
402 RETURN_COPY(callable);
403 }
404
405 if (zend_create_closure_from_callable(return_value, callable, &error) == FAILURE) {
406 if (error) {
407 zend_type_error("Failed to create closure from callable: %s", error);
408 efree(error);
409 } else {
410 zend_type_error("Failed to create closure from callable");
411 }
412 }
413}
414/* }}} */
415
416static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
417{
418 zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
419 return NULL;
420}
421/* }}} */
422
423/* int return due to Object Handler API */
424static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
425{
427
428 zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1);
429 zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2);
430
432 return ZEND_UNCOMPARABLE;
433 }
434
435 if (Z_TYPE(lhs->this_ptr) != Z_TYPE(rhs->this_ptr)) {
436 return ZEND_UNCOMPARABLE;
437 }
438
439 if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) {
440 return ZEND_UNCOMPARABLE;
441 }
442
443 if (lhs->called_scope != rhs->called_scope) {
444 return ZEND_UNCOMPARABLE;
445 }
446
447 if (lhs->func.type != rhs->func.type) {
448 return ZEND_UNCOMPARABLE;
449 }
450
451 if (lhs->func.common.scope != rhs->func.common.scope) {
452 return ZEND_UNCOMPARABLE;
453 }
454
455 if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) {
456 return ZEND_UNCOMPARABLE;
457 }
458
459 return 0;
460}
461/* }}} */
462
464{
465 zend_closure *closure = (zend_closure *)object;
467 const uint32_t keep_flags =
469
470 invoke->common = closure->func.common;
471 /* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the
472 * same as for ZEND_USER_FUNCTION (uses zend_string* instead of char*).
473 * This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set,
474 * and we won't check arguments on internal function. We also set
475 * ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
480 invoke->internal_function.fn_flags |=
482 }
483 invoke->internal_function.handler = ZEND_MN(Closure___invoke);
485 invoke->internal_function.module = 0;
487 invoke->internal_function.function_name = ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE);
488 return invoke;
489}
490/* }}} */
491
493{
494 zend_closure *closure = (zend_closure *) obj;
495 return &closure->func;
496}
497/* }}} */
498
500{
501 zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
502 return &closure->this_ptr;
503}
504/* }}} */
505
506static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */
507{
509 return zend_get_closure_invoke_method(*object);
510 }
511
512 return zend_std_get_method(object, method, key);
513}
514/* }}} */
515
516static void zend_closure_free_storage(zend_object *object) /* {{{ */
517{
518 zend_closure *closure = (zend_closure *)object;
519
520 zend_object_std_dtor(&closure->std);
521
522 if (closure->func.type == ZEND_USER_FUNCTION) {
523 /* We don't own the static variables of fake closures. */
524 if (!(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) {
527 }
528 destroy_op_array(&closure->func.op_array);
529 } else if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
530 zend_string_release(closure->func.common.function_name);
531 }
532
533 if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
534 zval_ptr_dtor(&closure->this_ptr);
535 }
536}
537/* }}} */
538
539static zend_object *zend_closure_new(zend_class_entry *class_type) /* {{{ */
540{
541 zend_closure *closure;
542
543 closure = emalloc(sizeof(zend_closure));
544 memset(closure, 0, sizeof(zend_closure));
545
546 zend_object_std_init(&closure->std, class_type);
547
548 return (zend_object*)closure;
549}
550/* }}} */
551
552static zend_object *zend_closure_clone(zend_object *zobject) /* {{{ */
553{
554 zend_closure *closure = (zend_closure *)zobject;
555 zval result;
556
557 zend_create_closure(&result, &closure->func,
558 closure->func.common.scope, closure->called_scope, &closure->this_ptr);
559 return Z_OBJ(result);
560}
561/* }}} */
562
563static zend_result zend_closure_get_closure(zend_object *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr, bool check_only) /* {{{ */
564{
565 zend_closure *closure = (zend_closure*)obj;
566
567 *fptr_ptr = &closure->func;
568 *ce_ptr = closure->called_scope;
569
570 if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
571 *obj_ptr = Z_OBJ(closure->this_ptr);
572 } else {
573 *obj_ptr = NULL;
574 }
575
576 return SUCCESS;
577}
578/* }}} */
579
580/* *is_temp is int due to Object Handler API */
581static HashTable *zend_closure_get_debug_info(zend_object *object, int *is_temp) /* {{{ */
582{
583 zend_closure *closure = (zend_closure *)object;
584 zval val;
585 struct _zend_arg_info *arg_info = closure->func.common.arg_info;
586 HashTable *debug_info;
587 bool zstr_args = (closure->func.type == ZEND_USER_FUNCTION) || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO);
588
589 *is_temp = 1;
590
591 debug_info = zend_new_array(8);
592
594 if (closure->func.common.scope) {
595 zend_string *class_name = closure->func.common.scope->name;
598 ZSTR_VAL(class_name), ZSTR_LEN(class_name),
599 "::", strlen("::"),
601 );
602 ZVAL_STR(&val, combined);
603 } else {
605 }
606 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_FUNCTION), &val);
607 } else {
609 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_NAME), &val);
610
612 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_FILE), &val);
613
615 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_LINE), &val);
616 }
617
618 if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
619 zval *var;
621 HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
622
623 array_init(&val);
624
625 ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(static_variables, key, var) {
626 zval copy;
627
628 if (Z_ISREF_P(var) && Z_REFCOUNT_P(var) == 1) {
629 var = Z_REFVAL_P(var);
630 }
631 ZVAL_COPY(&copy, var);
632
635
636 if (zend_hash_num_elements(Z_ARRVAL(val))) {
637 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_STATIC), &val);
638 } else {
640 }
641 }
642
643 if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
644 Z_ADDREF(closure->this_ptr);
645 zend_hash_update(debug_info, ZSTR_KNOWN(ZEND_STR_THIS), &closure->this_ptr);
646 }
647
648 if (arg_info &&
649 (closure->func.common.num_args ||
650 (closure->func.common.fn_flags & ZEND_ACC_VARIADIC))) {
651 uint32_t i, num_args, required = closure->func.common.required_num_args;
652
653 array_init(&val);
654
655 num_args = closure->func.common.num_args;
656 if (closure->func.common.fn_flags & ZEND_ACC_VARIADIC) {
657 num_args++;
658 }
659 for (i = 0; i < num_args; i++) {
661 zval info;
662 ZEND_ASSERT(arg_info->name && "Argument should have name");
663 if (zstr_args) {
664 name = zend_strpprintf(0, "%s$%s",
665 ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
666 ZSTR_VAL(arg_info->name));
667 } else {
668 name = zend_strpprintf(0, "%s$%s",
669 ZEND_ARG_SEND_MODE(arg_info) ? "&" : "",
670 ((zend_internal_arg_info*)arg_info)->name);
671 }
672 ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
675 arg_info++;
676 }
677 zend_hash_str_update(debug_info, "parameter", sizeof("parameter")-1, &val);
678 }
679
680 return debug_info;
681}
682/* }}} */
683
684static HashTable *zend_closure_get_gc(zend_object *obj, zval **table, int *n) /* {{{ */
685{
686 zend_closure *closure = (zend_closure *)obj;
687
688 *table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
689 *n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
690 /* Fake closures don't own the static variables they reference. */
691 return (closure->func.type == ZEND_USER_FUNCTION
692 && !(closure->func.op_array.fn_flags & ZEND_ACC_FAKE_CLOSURE)) ?
693 ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr) : NULL;
694}
695/* }}} */
696
697/* {{{ Private constructor preventing instantiation */
699{
700 zend_throw_error(NULL, "Instantiation of class Closure is not allowed");
701}
702/* }}} */
703
704void zend_register_closure_ce(void) /* {{{ */
705{
706 zend_ce_closure = register_class_Closure();
707 zend_ce_closure->create_object = zend_closure_new;
708 zend_ce_closure->default_object_handlers = &closure_handlers;
709
710 memcpy(&closure_handlers, &std_object_handlers, sizeof(zend_object_handlers));
711 closure_handlers.free_obj = zend_closure_free_storage;
712 closure_handlers.get_constructor = zend_closure_get_constructor;
713 closure_handlers.get_method = zend_closure_get_method;
714 closure_handlers.compare = zend_closure_compare;
715 closure_handlers.clone_obj = zend_closure_clone;
716 closure_handlers.get_debug_info = zend_closure_get_debug_info;
717 closure_handlers.get_closure = zend_closure_get_closure;
718 closure_handlers.get_gc = zend_closure_get_gc;
719}
720/* }}} */
721
722static ZEND_NAMED_FUNCTION(zend_closure_internal_handler) /* {{{ */
723{
726 // Assign to EX(this) so that it is released after observer checks etc.
728 Z_OBJ(EX(This)) = &closure->std;
729}
730/* }}} */
731
732static void zend_create_closure_ex(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr, bool is_fake) /* {{{ */
733{
734 zend_closure *closure;
735 void *ptr;
736
738
739 closure = (zend_closure *)Z_OBJ_P(res);
740
741 if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
742 /* use dummy scope if we're binding an object without specifying a scope */
743 /* maybe it would be better to create one for this purpose */
745 }
746
747 if (func->type == ZEND_USER_FUNCTION) {
748 memcpy(&closure->func, func, sizeof(zend_op_array));
751
752 zend_string_addref(closure->func.op_array.function_name);
753 if (closure->func.op_array.refcount) {
754 (*closure->func.op_array.refcount)++;
755 }
756
757 /* For fake closures, we want to reuse the static variables of the original function. */
758 if (!is_fake) {
759 if (closure->func.op_array.static_variables) {
762 }
763 ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr,
765 } else if (func->op_array.static_variables) {
766 HashTable *ht = ZEND_MAP_PTR_GET(func->op_array.static_variables_ptr);
767
768 if (!ht) {
769 ht = zend_array_dup(func->op_array.static_variables);
770 ZEND_MAP_PTR_SET(func->op_array.static_variables_ptr, ht);
771 }
772 ZEND_MAP_PTR_INIT(closure->func.op_array.static_variables_ptr, ht);
773 }
774
775 /* Runtime cache is scope-dependent, so we cannot reuse it if the scope changed */
776 ptr = ZEND_MAP_PTR_GET(func->op_array.run_time_cache);
777 if (!ptr
778 || func->common.scope != scope
779 || (func->common.fn_flags & ZEND_ACC_HEAP_RT_CACHE)
780 ) {
781 if (!ptr
782 && (func->common.fn_flags & ZEND_ACC_CLOSURE)
783 && (func->common.scope == scope ||
784 !(func->common.fn_flags & ZEND_ACC_IMMUTABLE))) {
785 /* If a real closure is used for the first time, we create a shared runtime cache
786 * and remember which scope it is for. */
787 if (func->common.scope != scope) {
788 func->common.scope = scope;
789 }
790 ptr = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
791 ZEND_MAP_PTR_SET(func->op_array.run_time_cache, ptr);
793 } else {
794 /* Otherwise, we use a non-shared runtime cache */
795 ptr = emalloc(func->op_array.cache_size);
797 }
798 memset(ptr, 0, func->op_array.cache_size);
799 }
800 ZEND_MAP_PTR_INIT(closure->func.op_array.run_time_cache, ptr);
801 } else {
802 memcpy(&closure->func, func, sizeof(zend_internal_function));
804 /* wrap internal function handler to avoid memory leak */
805 if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
806 /* avoid infinity recursion, by taking handler from nested closure */
807 zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
808 ZEND_ASSERT(nested->std.ce == zend_ce_closure);
810 } else {
812 }
813 closure->func.internal_function.handler = zend_closure_internal_handler;
814 zend_string_addref(closure->func.op_array.function_name);
815 if (!func->common.scope) {
816 /* if it's a free function, we won't set scope & this since they're meaningless */
817 this_ptr = NULL;
818 scope = NULL;
819 }
820 }
821
822 ZVAL_UNDEF(&closure->this_ptr);
823 /* Invariant:
824 * If the closure is unscoped or static, it has no bound object. */
825 closure->func.common.scope = scope;
826 closure->called_scope = called_scope;
827 if (scope) {
829 if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
830 ZVAL_OBJ_COPY(&closure->this_ptr, Z_OBJ_P(this_ptr));
831 }
832 }
833}
834/* }}} */
835
837{
838 zend_create_closure_ex(res, func, scope, called_scope, this_ptr,
839 /* is_fake */ (func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) != 0);
840}
841
843{
844 zend_closure *closure;
845
846 zend_create_closure_ex(res, func, scope, called_scope, this_ptr, /* is_fake */ true);
847
848 closure = (zend_closure *)Z_OBJ_P(res);
850}
851/* }}} */
852
853/* __call and __callStatic name the arguments "$arguments" in the docs. */
854static zend_internal_arg_info trampoline_arg_info[] = {ZEND_ARG_VARIADIC_TYPE_INFO(false, arguments, IS_MIXED, false)};
855
857 zval instance;
858 zend_internal_function trampoline;
859 zend_function *mptr = call->func;
860
863 }
864
867 (Z_OBJCE(call->This) == zend_ce_closure)
868 && zend_string_equals(mptr->common.function_name, ZSTR_KNOWN(ZEND_STR_MAGIC_INVOKE))) {
870 RETURN_OBJ_COPY(Z_OBJ(call->This));
871 }
872
873 memset(&trampoline, 0, sizeof(zend_internal_function));
874 trampoline.type = ZEND_INTERNAL_FUNCTION;
876 trampoline.handler = zend_closure_call_magic;
877 trampoline.function_name = mptr->common.function_name;
878 trampoline.scope = mptr->common.scope;
879 trampoline.doc_comment = NULL;
880 if (trampoline.fn_flags & ZEND_ACC_VARIADIC) {
881 trampoline.arg_info = trampoline_arg_info;
882 }
883 trampoline.attributes = mptr->common.attributes;
884
886 mptr = (zend_function *) &trampoline;
887 }
888
890 ZVAL_OBJ(&instance, Z_OBJ(call->This));
891
892 zend_create_fake_closure(return_value, mptr, mptr->common.scope, Z_OBJCE(instance), &instance);
893 } else {
895 }
896
897 if (&mptr->internal_function == &trampoline) {
898 zend_string_release(mptr->common.function_name);
899 }
900} /* }}} */
901
902void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var) /* {{{ */
903{
904 zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
905 HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
906 zend_hash_update(static_variables, var_name, var);
907}
908/* }}} */
909
910void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val) /* {{{ */
911{
912 zend_closure *closure = (zend_closure *) Z_OBJ_P(closure_zv);
913 HashTable *static_variables = ZEND_MAP_PTR_GET(closure->func.op_array.static_variables_ptr);
914 zval *var = (zval*)((char*)static_variables->arData + offset);
915 zval_ptr_dtor(var);
916 ZVAL_COPY_VALUE(var, val);
917}
918/* }}} */
copy(string $from, string $to, $context=null)
error($message)
Definition ext_skel.php:22
zend_long n
Definition ffi.c:4979
zend_string * res
Definition ffi.c:4692
void * ptr
Definition ffi.c:3814
memcpy(ptr1, ptr2, size)
memset(ptr, 0, type->size)
zval * val
Definition ffi.c:4262
HashTable * ht
Definition ffi.c:4838
zend_long offset
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
foreach($dp as $el) foreach( $dp as $el) if( $pass2< 2) echo ""
char * arena
Definition php_bcmath.h:37
unsigned char key[REFLECTION_KEY_LEN]
const char * func_name
zend_string * var_name
Definition session.c:966
zend_string * name
Bucket * arData
Definition zend_types.h:403
zend_string * name
Definition zend.h:149
zend_function * __call
Definition zend.h:179
zend_function * __callstatic
Definition zend.h:180
zend_class_entry * called_scope
zend_object std
zend_function func
zif_handler orig_internal_handler
zend_function * function_handler
Definition zend_API.h:60
zend_class_entry * called_scope
Definition zend_API.h:62
zend_object * object
Definition zend_API.h:63
HashTable * named_params
Definition zend_API.h:56
uint32_t param_count
Definition zend_API.h:51
zend_object * object
Definition zend_API.h:50
zend_class_entry * scope
zend_string * function_name
struct _zend_module_entry * module
zend_internal_arg_info * arg_info
zend_string * doc_comment
zend_refcounted_h gc
Definition zend_types.h:557
zend_class_entry * ce
Definition zend_types.h:560
uint32_t * refcount
HashTable * static_variables
zend_string * filename
uint32_t line_start
zend_string * function_name
uint32_t fn_flags
union _zend_refcounted_h::@314043213170051131205344315207200231050160043145 u
zend_arg_info * arg_info
uint32_t required_num_args
zend_class_entry * scope
zend_op_array op_array
zend_string * function_name
struct _zend_function::@236135173067030250234125302313220025134003177336 common
HashTable * attributes
uint32_t fn_flags
zend_internal_function internal_function
uint32_t num_args
ZEND_API zend_string * zend_strpprintf(size_t max_len, const char *format,...)
Definition zend.c:353
ZEND_API ZEND_COLD void zend_throw_error(zend_class_entry *exception_ce, const char *format,...)
Definition zend.c:1772
ZEND_API ZEND_COLD void zend_type_error(const char *format,...)
Definition zend.c:1824
ZEND_API ZEND_COLD void zend_error(int type, const char *format,...)
Definition zend.c:1666
#define INTERNAL_FUNCTION_PARAM_PASSTHRU
Definition zend.h:50
ZEND_API zend_result zend_copy_parameters_array(uint32_t param_count, zval *argument_array)
Definition zend_API.c:72
ZEND_API zend_result object_init_ex(zval *arg, zend_class_entry *class_type)
Definition zend_API.c:1849
ZEND_API bool zend_is_callable_ex(zval *callable, zend_object *object, uint32_t check_flags, zend_string **callable_name, zend_fcall_info_cache *fcc, char **error)
Definition zend_API.c:4271
#define ZEND_NUM_ARGS()
Definition zend_API.h:530
struct _zend_fcall_info_cache zend_fcall_info_cache
#define RETURN_OBJ(r)
Definition zend_API.h:1052
#define RETURN_COPY(zv)
Definition zend_API.h:1054
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define call_user_function_named(function_table, object, function_name, retval_ptr, param_count, params, named_params)
Definition zend_API.h:690
#define Z_PARAM_OBJECT(dest)
Definition zend_API.h:1940
#define RETURN_NULL()
Definition zend_API.h:1036
#define array_init_size(arg, size)
Definition zend_API.h:538
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define RETVAL_OBJ_COPY(r)
Definition zend_API.h:1027
#define RETURN_OBJ_COPY(r)
Definition zend_API.h:1053
struct _zend_fcall_info zend_fcall_info
#define ZEND_METHOD(classname, name)
Definition zend_API.h:76
#define ZEND_ARG_VARIADIC_TYPE_INFO(pass_by_ref, name, type_hint, allow_null)
Definition zend_API.h:142
#define ZEND_THIS
Definition zend_API.h:523
#define ZEND_NAMED_FUNCTION(name)
Definition zend_API.h:74
#define Z_PARAM_OBJECT_OF_CLASS(dest, _ce)
Definition zend_API.h:1976
#define Z_PARAM_OBJ_OR_STR_OR_NULL(destination_object, destination_string)
Definition zend_API.h:1771
#define Z_PARAM_ZVAL(dest)
Definition zend_API.h:2100
#define RETVAL_FALSE
Definition zend_API.h:1032
ZEND_API zend_result zend_call_function(zend_fcall_info *fci, zend_fcall_info_cache *fci_cache)
#define ZEND_MN(name)
Definition zend_API.h:72
#define Z_PARAM_OBJECT_OR_NULL(dest)
Definition zend_API.h:1943
#define array_init(arg)
Definition zend_API.h:537
#define Z_PARAM_VARIADIC_WITH_NAMED(dest, dest_num, dest_named)
Definition zend_API.h:2127
#define efree_size(ptr, size)
Definition zend_alloc.h:138
#define efree(ptr)
Definition zend_alloc.h:155
#define emalloc(size)
Definition zend_alloc.h:151
struct _zval_struct zval
strlen(string $string)
void zend_register_closure_ce(void)
ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
ZEND_API const zend_function * zend_get_closure_method_def(zend_object *obj)
ZEND_PARSE_PARAMETERS_END()
uint32_t num_args
ZEND_API zend_class_entry * zend_ce_closure
struct _zend_closure zend_closure
HashTable * named_args
ZEND_API zval * zend_get_closure_this_ptr(zval *obj)
zend_string_release_ex(func->internal_function.function_name, 0)
ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr)
execute_data func
void zend_closure_bind_var_ex(zval *closure_zv, uint32_t offset, zval *val)
void zend_closure_from_frame(zval *return_value, zend_execute_data *call)
zval * args
void zend_closure_bind_var(zval *closure_zv, zend_string *var_name, zval *var)
ZEND_API zend_function * zend_get_closure_invoke_method(zend_object *object)
#define ZEND_CLOSURE_OBJECT(op_array)
#define ZEND_ACC_USES_THIS
#define ZEND_CALL_CLOSURE
#define ZEND_USER_CODE(type)
#define ZEND_ACC_FAKE_CLOSURE
#define ZEND_ACC_IMMUTABLE
ZEND_API void destroy_op_array(zend_op_array *op_array)
#define ZEND_INTERNAL_FUNCTION
#define ZEND_ARG_SEND_MODE(arg_info)
#define ZEND_ACC_USER_ARG_INFO
#define EX(element)
#define ZEND_USER_FUNCTION
#define EX_CALL_INFO()
#define ZEND_ACC_CALL_VIA_TRAMPOLINE
#define ZEND_ACC_GENERATOR
struct _zend_op_array zend_op_array
#define ZEND_CALL_HAS_THIS
#define ZEND_CALL_HAS_EXTRA_NAMED_PARAMS
ZEND_API void zend_destroy_static_vars(zend_op_array *op_array)
#define ZEND_ACC_CALL_VIA_HANDLER
struct _zend_internal_arg_info zend_internal_arg_info
#define ZEND_ACC_STATIC
#define ZEND_CALL_INFO(call)
#define ZEND_ACC_PUBLIC
#define ZEND_ACC_HAS_RETURN_TYPE
#define ZEND_ACC_HEAP_RT_CACHE
#define ZEND_ACC_VARIADIC
#define ZEND_ADD_CALL_FLAG(call, flag)
#define ZEND_INTERNAL_CLASS
struct _zend_internal_function zend_internal_function
#define ZEND_ACC_DEPRECATED
#define ZEND_CALL_RELEASE_THIS
#define ZEND_INVOKE_FUNC_NAME
#define ZEND_ACC_RETURN_REFERENCE
void(ZEND_FASTCALL * zif_handler)(INTERNAL_FUNCTION_PARAMETERS)
#define ZEND_ACC_CLOSURE
#define ZEND_API
#define E_WARNING
Definition zend_errors.h:24
ZEND_API zend_class_entry * zend_get_called_scope(zend_execute_data *ex)
ZEND_API zend_class_entry * zend_lookup_class(zend_string *name)
union _zend_function zend_function
#define CG(v)
#define EG(v)
ZEND_API zval *ZEND_FASTCALL zend_hash_add_new(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:1007
ZEND_API zval *ZEND_FASTCALL zend_hash_str_update(HashTable *ht, const char *str, size_t len, zval *pData)
Definition zend_hash.c:1031
ZEND_API HashTable *ZEND_FASTCALL zend_array_dup(HashTable *source)
Definition zend_hash.c:2438
ZEND_API zval *ZEND_FASTCALL zend_hash_update(HashTable *ht, zend_string *key, zval *pData)
Definition zend_hash.c:997
ZEND_API void ZEND_FASTCALL zend_hash_real_init_mixed(HashTable *ht)
Definition zend_hash.c:338
#define zend_new_array(size)
Definition zend_hash.h:338
#define ZEND_HASH_FOREACH_END()
Definition zend_hash.h:1086
#define ZVAL_EMPTY_ARRAY(z)
Definition zend_hash.h:87
#define ZEND_HASH_MAP_FOREACH_STR_KEY_VAL(ht, _key, _val)
Definition zend_hash.h:1374
struct _zend_string zend_string
#define ZEND_MAP_PTR_INIT(ptr, val)
#define ZEND_MAP_PTR_GET(ptr)
#define ZEND_MAP_PTR(ptr)
#define ZEND_MAP_PTR_SET(ptr, val)
ZEND_API zend_function * zend_std_get_method(zend_object **obj_ptr, zend_string *method_name, const zval *key)
ZEND_API const zend_object_handlers std_object_handlers
#define ZEND_COMPARE_OBJECTS_FALLBACK(op1, op2)
#define zend_free_trampoline(func)
ZEND_API void ZEND_FASTCALL zend_object_std_init(zend_object *object, zend_class_entry *ce)
ZEND_API void zend_object_std_dtor(zend_object *object)
#define ZEND_UNCOMPARABLE
#define XtOffsetOf(s_type, field)
#define ZEND_ASSERT(c)
#define ZEND_COLD
#define UNEXPECTED(condition)
struct _zend_class_entry zend_class_entry
struct _zend_object zend_object
ZEND_API zend_string * zend_string_concat3(const char *str1, size_t str1_len, const char *str2, size_t str2_len, const char *str3, size_t str3_len)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_KNOWN(idx)
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define zend_string_equals_literal_ci(str, c)
#define Z_TYPE_P(zval_p)
Definition zend_types.h:660
#define ZVAL_STR(z, s)
#define Z_ISREF_P(zval_p)
Definition zend_types.h:954
#define Z_TRY_ADDREF_P(pz)
#define ZVAL_UNDEF(z)
#define Z_REFVAL_P(zval_p)
#define IS_UNDEF
Definition zend_types.h:600
#define GC_NULL
Definition zend_types.h:784
#define ZVAL_LONG(z, l)
#define Z_CE(zval)
#define ZVAL_STR_COPY(z, s)
struct _zend_array HashTable
Definition zend_types.h:386
#define Z_OBJ_P(zval_p)
Definition zend_types.h:990
#define Z_ISUNDEF(zval)
Definition zend_types.h:956
#define GC_DELREF(p)
Definition zend_types.h:710
#define IS_NULL
Definition zend_types.h:601
#define Z_OBJCE_P(zval_p)
#define ZVAL_OBJ(z, o)
@ FAILURE
Definition zend_types.h:61
#define IS_OBJECT
Definition zend_types.h:608
#define Z_ADDREF(z)
#define IS_MIXED
Definition zend_types.h:619
#define ZVAL_NEW_STR(z, s)
#define ZVAL_COPY(z, v)
#define ZVAL_OBJ_COPY(z, o)
#define Z_REFCOUNT_P(pz)
ZEND_RESULT_CODE zend_result
Definition zend_types.h:64
#define Z_ISREF(zval)
Definition zend_types.h:953
struct _zend_object_handlers zend_object_handlers
Definition zend_types.h:88
#define Z_TYPE(zval)
Definition zend_types.h:659
#define Z_ARRVAL(zval)
Definition zend_types.h:986
struct _zend_execute_data zend_execute_data
Definition zend_types.h:91
#define ZVAL_COPY_VALUE(z, v)
#define Z_OBJCE(zval)
#define Z_OBJ(zval)
Definition zend_types.h:989
ZEND_API void zval_ptr_dtor(zval *zval_ptr)
zval * return_value
zend_string * name
bool result
execute_data
zend_execute_data * call
new_op_array scope