root/Zend/zend_closures.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ZEND_METHOD
  2. zend_valid_closure_binding
  3. ZEND_METHOD
  4. ZEND_METHOD
  5. zend_closure_get_constructor
  6. zend_closure_compare_objects
  7. zend_get_closure_invoke_method
  8. zend_get_closure_method_def
  9. zend_get_closure_this_ptr
  10. zend_closure_get_method
  11. zend_closure_read_property
  12. zend_closure_write_property
  13. zend_closure_get_property_ptr_ptr
  14. zend_closure_has_property
  15. zend_closure_unset_property
  16. zend_closure_free_storage
  17. zend_closure_new
  18. zend_closure_clone
  19. zend_closure_get_closure
  20. zend_closure_get_debug_info
  21. zend_closure_get_gc
  22. ZEND_METHOD
  23. zend_register_closure_ce
  24. zend_closure_internal_handler
  25. zend_create_closure
  26. zend_create_fake_closure

   1 /*
   2    +----------------------------------------------------------------------+
   3    | Zend Engine                                                          |
   4    +----------------------------------------------------------------------+
   5    | Copyright (c) 1998-2016 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@zend.com>                             |
  17    |          Marcus Boerger <helly@php.net>                              |
  18    +----------------------------------------------------------------------+
  19 */
  20 
  21 /* $Id$ */
  22 
  23 #include "zend.h"
  24 #include "zend_API.h"
  25 #include "zend_closures.h"
  26 #include "zend_exceptions.h"
  27 #include "zend_interfaces.h"
  28 #include "zend_objects.h"
  29 #include "zend_objects_API.h"
  30 #include "zend_globals.h"
  31 
  32 #define ZEND_CLOSURE_PRINT_NAME "Closure object"
  33 
  34 #define ZEND_CLOSURE_PROPERTY_ERROR() \
  35         zend_throw_error(NULL, "Closure object cannot have properties")
  36 
  37 /* reuse bit to mark "fake" closures (it wasn't used for functions before) */
  38 #define ZEND_ACC_FAKE_CLOSURE ZEND_ACC_INTERFACE
  39 
  40 typedef struct _zend_closure {
  41         zend_object       std;
  42         zend_function     func;
  43         zval              this_ptr;
  44         zend_class_entry *called_scope;
  45         void (*orig_internal_handler)(INTERNAL_FUNCTION_PARAMETERS);
  46 } zend_closure;
  47 
  48 /* non-static since it needs to be referenced */
  49 ZEND_API zend_class_entry *zend_ce_closure;
  50 static zend_object_handlers closure_handlers;
  51 
  52 ZEND_METHOD(Closure, __invoke) /* {{{ */
  53 {
  54         zend_function *func = EX(func);
  55         zval *arguments;
  56 
  57         arguments = emalloc(sizeof(zval) * ZEND_NUM_ARGS());
  58         if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
  59                 efree(arguments);
  60                 zend_throw_error(NULL, "Cannot get arguments for calling closure");
  61                 RETVAL_FALSE;
  62         } else if (call_user_function_ex(CG(function_table), NULL, getThis(), return_value, ZEND_NUM_ARGS(), arguments, 1, NULL) == FAILURE) {
  63                 RETVAL_FALSE;
  64         }
  65         efree(arguments);
  66 
  67         /* destruct the function also, then - we have allocated it in get_method */
  68         zend_string_release(func->internal_function.function_name);
  69         efree(func);
  70 #if ZEND_DEBUG
  71         execute_data->func = NULL;
  72 #endif
  73 }
  74 /* }}} */
  75 
  76 static zend_bool zend_valid_closure_binding(
  77                 zend_closure *closure, zval *newthis, zend_class_entry *scope) /* {{{ */
  78 {
  79         zend_function *func = &closure->func;
  80         if (newthis) {
  81                 if (func->common.fn_flags & ZEND_ACC_STATIC) {
  82                         zend_error(E_WARNING, "Cannot bind an instance to a static closure");
  83                         return 0;
  84                 }
  85 
  86                 if (func->type == ZEND_INTERNAL_FUNCTION && func->common.scope &&
  87                                 !instanceof_function(Z_OBJCE_P(newthis), func->common.scope)) {
  88                         /* Binding incompatible $this to an internal method is not supported. */
  89                         zend_error(E_WARNING, "Cannot bind internal method %s::%s() to object of class %s",
  90                                         ZSTR_VAL(func->common.scope->name),
  91                                         ZSTR_VAL(func->common.function_name),
  92                                         ZSTR_VAL(Z_OBJCE_P(newthis)->name));
  93                         return 0;
  94                 }
  95         } else if (!(func->common.fn_flags & ZEND_ACC_STATIC) && func->common.scope
  96                         && func->type == ZEND_INTERNAL_FUNCTION) {
  97                 zend_error(E_WARNING, "Cannot unbind $this of internal method");
  98                 return 0;
  99         }
 100 
 101         if (scope && scope != func->common.scope && scope->type == ZEND_INTERNAL_CLASS) {
 102                 /* rebinding to internal class is not allowed */
 103                 zend_error(E_WARNING, "Cannot bind closure to scope of internal class %s",
 104                                 ZSTR_VAL(scope->name));
 105                 return 0;
 106         }
 107 
 108         if ((func->common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && scope != func->common.scope) {
 109                 zend_error(E_WARNING, "Cannot rebind scope of closure created by ReflectionFunctionAbstract::getClosure()");
 110                 return 0;
 111         }
 112 
 113         return 1;
 114 }
 115 /* }}} */
 116 
 117 /* {{{ proto mixed Closure::call(object to [, mixed parameter] [, mixed ...] )
 118    Call closure, binding to a given object with its class as the scope */
 119 ZEND_METHOD(Closure, call)
 120 {
 121         zval *zclosure, *newthis, closure_result;
 122         zend_closure *closure;
 123         zend_fcall_info fci;
 124         zend_fcall_info_cache fci_cache;
 125         zval *my_params;
 126         int my_param_count = 0;
 127         zend_function my_function;
 128         zend_object *newobj;
 129 
 130         if (zend_parse_parameters(ZEND_NUM_ARGS(), "o*", &newthis, &my_params, &my_param_count) == FAILURE) {
 131                 return;
 132         }
 133 
 134         zclosure = getThis();
 135         closure = (zend_closure *) Z_OBJ_P(zclosure);
 136 
 137         newobj = Z_OBJ_P(newthis);
 138 
 139         if (!zend_valid_closure_binding(closure, newthis, Z_OBJCE_P(newthis))) {
 140                 return;
 141         }
 142 
 143         /* This should never happen as closures will always be callable */
 144         if (zend_fcall_info_init(zclosure, 0, &fci, &fci_cache, NULL, NULL) != SUCCESS) {
 145                 ZEND_ASSERT(0);
 146         }
 147 
 148         fci.retval = &closure_result;
 149         fci.params = my_params;
 150         fci.param_count = my_param_count;
 151         fci.object = fci_cache.object = newobj;
 152         fci_cache.initialized = 1;
 153         fci_cache.called_scope = Z_OBJCE_P(newthis);
 154 
 155         if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
 156                 zval new_closure;
 157                 zend_create_closure(&new_closure, fci_cache.function_handler, Z_OBJCE_P(newthis), closure->called_scope, newthis);
 158                 closure = (zend_closure *) Z_OBJ(new_closure);
 159                 fci_cache.function_handler = &closure->func;
 160         } else {
 161                 memcpy(&my_function, fci_cache.function_handler, fci_cache.function_handler->type == ZEND_USER_FUNCTION ? sizeof(zend_op_array) : sizeof(zend_internal_function));
 162                 /* use scope of passed object */
 163                 my_function.common.scope = Z_OBJCE_P(newthis);
 164                 fci_cache.function_handler = &my_function;
 165 
 166                 /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
 167                 if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
 168                         my_function.op_array.run_time_cache = emalloc(my_function.op_array.cache_size);
 169                         memset(my_function.op_array.run_time_cache, 0, my_function.op_array.cache_size);
 170                 }
 171         }
 172 
 173         if (zend_call_function(&fci, &fci_cache) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
 174                 ZVAL_COPY_VALUE(return_value, &closure_result);
 175         }
 176 
 177         if (fci_cache.function_handler->common.fn_flags & ZEND_ACC_GENERATOR) {
 178                 /* copied upon generator creation */
 179                 --GC_REFCOUNT(&closure->std);
 180         } else if (ZEND_USER_CODE(my_function.type) && closure->func.common.scope != Z_OBJCE_P(newthis)) {
 181                 efree(my_function.op_array.run_time_cache);
 182         }
 183 }
 184 /* }}} */
 185 
 186 /* {{{ proto Closure Closure::bind(callable old, object to [, mixed scope])
 187    Create a closure from another one and bind to another object and scope */
 188 ZEND_METHOD(Closure, bind)
 189 {
 190         zval *newthis, *zclosure, *scope_arg = NULL;
 191         zend_closure *closure, *new_closure;
 192         zend_class_entry *ce, *called_scope;
 193 
 194         if (zend_parse_method_parameters(ZEND_NUM_ARGS(), getThis(), "Oo!|z", &zclosure, zend_ce_closure, &newthis, &scope_arg) == FAILURE) {
 195                 return;
 196         }
 197 
 198         closure = (zend_closure *)Z_OBJ_P(zclosure);
 199 
 200         if (scope_arg != NULL) { /* scope argument was given */
 201                 if (Z_TYPE_P(scope_arg) == IS_OBJECT) {
 202                         ce = Z_OBJCE_P(scope_arg);
 203                 } else if (Z_TYPE_P(scope_arg) == IS_NULL) {
 204                         ce = NULL;
 205                 } else {
 206                         zend_string *class_name = zval_get_string(scope_arg);
 207                         if (zend_string_equals_literal(class_name, "static")) {
 208                                 ce = closure->func.common.scope;
 209                         } else if ((ce = zend_lookup_class_ex(class_name, NULL, 1)) == NULL) {
 210                                 zend_error(E_WARNING, "Class '%s' not found", ZSTR_VAL(class_name));
 211                                 zend_string_release(class_name);
 212                                 RETURN_NULL();
 213                         }
 214                         zend_string_release(class_name);
 215                 }
 216         } else { /* scope argument not given; do not change the scope by default */
 217                 ce = closure->func.common.scope;
 218         }
 219 
 220         if (!zend_valid_closure_binding(closure, newthis, ce)) {
 221                 return;
 222         }
 223 
 224         if (newthis) {
 225                 called_scope = Z_OBJCE_P(newthis);
 226         } else {
 227                 called_scope = ce;
 228         }
 229 
 230         zend_create_closure(return_value, &closure->func, ce, called_scope, newthis);
 231         new_closure = (zend_closure *) Z_OBJ_P(return_value);
 232 
 233         /* Runtime cache relies on bound scope to be immutable, hence we need a separate rt cache in case scope changed */
 234         if (ZEND_USER_CODE(closure->func.type) && (closure->func.common.scope != new_closure->func.common.scope || (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA))) {
 235                 new_closure->func.op_array.run_time_cache = emalloc(new_closure->func.op_array.cache_size);
 236                 memset(new_closure->func.op_array.run_time_cache, 0, new_closure->func.op_array.cache_size);
 237 
 238                 new_closure->func.op_array.fn_flags |= ZEND_ACC_NO_RT_ARENA;
 239         }
 240 }
 241 /* }}} */
 242 
 243 static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */
 244 {
 245         zend_throw_error(NULL, "Instantiation of 'Closure' is not allowed");
 246         return NULL;
 247 }
 248 /* }}} */
 249 
 250 static int zend_closure_compare_objects(zval *o1, zval *o2) /* {{{ */
 251 {
 252         return (Z_OBJ_P(o1) != Z_OBJ_P(o2));
 253 }
 254 /* }}} */
 255 
 256 ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object) /* {{{ */
 257 {
 258         zend_closure *closure = (zend_closure *)object;
 259         zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
 260         const uint32_t keep_flags =
 261                 ZEND_ACC_RETURN_REFERENCE | ZEND_ACC_VARIADIC | ZEND_ACC_HAS_RETURN_TYPE;
 262 
 263         invoke->common = closure->func.common;
 264         /* We return ZEND_INTERNAL_FUNCTION, but arg_info representation is the
 265          * same as for ZEND_USER_FUNCTION (uses zend_string* instead of char*).
 266          * This is not a problem, because ZEND_ACC_HAS_TYPE_HINTS is never set,
 267          * and we won't check arguments on internal function. We also set
 268          * ZEND_ACC_USER_ARG_INFO flag to prevent invalid usage by Reflection */
 269         invoke->type = ZEND_INTERNAL_FUNCTION;
 270         invoke->internal_function.fn_flags =
 271                 ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER | (closure->func.common.fn_flags & keep_flags);
 272         if (closure->func.type != ZEND_INTERNAL_FUNCTION || (closure->func.common.fn_flags & ZEND_ACC_USER_ARG_INFO)) {
 273                 invoke->internal_function.fn_flags |=
 274                         ZEND_ACC_USER_ARG_INFO;
 275         }
 276         invoke->internal_function.handler = ZEND_MN(Closure___invoke);
 277         invoke->internal_function.module = 0;
 278         invoke->internal_function.scope = zend_ce_closure;
 279         invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0);
 280         return invoke;
 281 }
 282 /* }}} */
 283 
 284 ZEND_API const zend_function *zend_get_closure_method_def(zval *obj) /* {{{ */
 285 {
 286         zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
 287         return &closure->func;
 288 }
 289 /* }}} */
 290 
 291 ZEND_API zval* zend_get_closure_this_ptr(zval *obj) /* {{{ */
 292 {
 293         zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
 294         return &closure->this_ptr;
 295 }
 296 /* }}} */
 297 
 298 static zend_function *zend_closure_get_method(zend_object **object, zend_string *method, const zval *key) /* {{{ */
 299 {
 300         zend_string *lc_name;
 301 
 302         lc_name = zend_string_tolower(method);
 303         if (zend_string_equals_literal(method, ZEND_INVOKE_FUNC_NAME)) {
 304                 zend_string_release(lc_name);
 305                 return zend_get_closure_invoke_method(*object);
 306         }
 307         zend_string_release(lc_name);
 308         return std_object_handlers.get_method(object, method, key);
 309 }
 310 /* }}} */
 311 
 312 static zval *zend_closure_read_property(zval *object, zval *member, int type, void **cache_slot, zval *rv) /* {{{ */
 313 {
 314         ZEND_CLOSURE_PROPERTY_ERROR();
 315         return &EG(uninitialized_zval);
 316 }
 317 /* }}} */
 318 
 319 static void zend_closure_write_property(zval *object, zval *member, zval *value, void **cache_slot) /* {{{ */
 320 {
 321         ZEND_CLOSURE_PROPERTY_ERROR();
 322 }
 323 /* }}} */
 324 
 325 static zval *zend_closure_get_property_ptr_ptr(zval *object, zval *member, int type, void **cache_slot) /* {{{ */
 326 {
 327         ZEND_CLOSURE_PROPERTY_ERROR();
 328         return NULL;
 329 }
 330 /* }}} */
 331 
 332 static int zend_closure_has_property(zval *object, zval *member, int has_set_exists, void **cache_slot) /* {{{ */
 333 {
 334         if (has_set_exists != 2) {
 335                 ZEND_CLOSURE_PROPERTY_ERROR();
 336         }
 337         return 0;
 338 }
 339 /* }}} */
 340 
 341 static void zend_closure_unset_property(zval *object, zval *member, void **cache_slot) /* {{{ */
 342 {
 343         ZEND_CLOSURE_PROPERTY_ERROR();
 344 }
 345 /* }}} */
 346 
 347 static void zend_closure_free_storage(zend_object *object) /* {{{ */
 348 {
 349         zend_closure *closure = (zend_closure *)object;
 350 
 351         zend_object_std_dtor(&closure->std);
 352 
 353         if (closure->func.type == ZEND_USER_FUNCTION) {
 354                 if (closure->func.op_array.fn_flags & ZEND_ACC_NO_RT_ARENA) {
 355                         efree(closure->func.op_array.run_time_cache);
 356                         closure->func.op_array.run_time_cache = NULL;
 357                 }
 358                 destroy_op_array(&closure->func.op_array);
 359         }
 360 
 361         if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
 362                 zval_ptr_dtor(&closure->this_ptr);
 363         }
 364 }
 365 /* }}} */
 366 
 367 static zend_object *zend_closure_new(zend_class_entry *class_type) /* {{{ */
 368 {
 369         zend_closure *closure;
 370 
 371         closure = emalloc(sizeof(zend_closure));
 372         memset(closure, 0, sizeof(zend_closure));
 373 
 374         zend_object_std_init(&closure->std, class_type);
 375         closure->std.handlers = &closure_handlers;
 376 
 377         return (zend_object*)closure;
 378 }
 379 /* }}} */
 380 
 381 static zend_object *zend_closure_clone(zval *zobject) /* {{{ */
 382 {
 383         zend_closure *closure = (zend_closure *)Z_OBJ_P(zobject);
 384         zval result;
 385 
 386         zend_create_closure(&result, &closure->func,
 387                 closure->func.common.scope, closure->called_scope, &closure->this_ptr);
 388         return Z_OBJ(result);
 389 }
 390 /* }}} */
 391 
 392 int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zend_object **obj_ptr) /* {{{ */
 393 {
 394         zend_closure *closure;
 395 
 396         if (Z_TYPE_P(obj) != IS_OBJECT) {
 397                 return FAILURE;
 398         }
 399 
 400         closure = (zend_closure *)Z_OBJ_P(obj);
 401         *fptr_ptr = &closure->func;
 402         *ce_ptr = closure->called_scope;
 403 
 404         if (obj_ptr) {
 405                 if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
 406                         *obj_ptr = Z_OBJ(closure->this_ptr);
 407                 } else {
 408                         *obj_ptr = NULL;
 409                 }
 410         }
 411         return SUCCESS;
 412 }
 413 /* }}} */
 414 
 415 static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp) /* {{{ */
 416 {
 417         zend_closure *closure = (zend_closure *)Z_OBJ_P(object);
 418         zval val;
 419         struct _zend_arg_info *arg_info = closure->func.common.arg_info;
 420         HashTable *debug_info;
 421 
 422         *is_temp = 1;
 423 
 424         ALLOC_HASHTABLE(debug_info);
 425         zend_hash_init(debug_info, 8, NULL, ZVAL_PTR_DTOR, 0);
 426 
 427         if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
 428                 HashTable *static_variables = closure->func.op_array.static_variables;
 429                 ZVAL_ARR(&val, zend_array_dup(static_variables));
 430                 zend_hash_str_update(debug_info, "static", sizeof("static")-1, &val);
 431         }
 432 
 433         if (Z_TYPE(closure->this_ptr) != IS_UNDEF) {
 434                 Z_ADDREF(closure->this_ptr);
 435                 zend_hash_str_update(debug_info, "this", sizeof("this")-1, &closure->this_ptr);
 436         }
 437 
 438         if (arg_info &&
 439                 (closure->func.common.num_args ||
 440                  (closure->func.common.fn_flags & ZEND_ACC_VARIADIC))) {
 441                 uint32_t i, num_args, required = closure->func.common.required_num_args;
 442 
 443                 array_init(&val);
 444 
 445                 num_args = closure->func.common.num_args;
 446                 if (closure->func.common.fn_flags & ZEND_ACC_VARIADIC) {
 447                         num_args++;
 448                 }
 449                 for (i = 0; i < num_args; i++) {
 450                         zend_string *name;
 451                         zval info;
 452                         if (arg_info->name) {
 453                                 name = zend_strpprintf(0, "%s$%s",
 454                                                 arg_info->pass_by_reference ? "&" : "",
 455                                                 ZSTR_VAL(arg_info->name));
 456                         } else {
 457                                 name = zend_strpprintf(0, "%s$param%d",
 458                                                 arg_info->pass_by_reference ? "&" : "",
 459                                                 i + 1);
 460                         }
 461                         ZVAL_NEW_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
 462                         zend_hash_update(Z_ARRVAL(val), name, &info);
 463                         zend_string_release(name);
 464                         arg_info++;
 465                 }
 466                 zend_hash_str_update(debug_info, "parameter", sizeof("parameter")-1, &val);
 467         }
 468 
 469         return debug_info;
 470 }
 471 /* }}} */
 472 
 473 static HashTable *zend_closure_get_gc(zval *obj, zval **table, int *n) /* {{{ */
 474 {
 475         zend_closure *closure = (zend_closure *)Z_OBJ_P(obj);
 476 
 477         *table = Z_TYPE(closure->this_ptr) != IS_NULL ? &closure->this_ptr : NULL;
 478         *n = Z_TYPE(closure->this_ptr) != IS_NULL ? 1 : 0;
 479         return (closure->func.type == ZEND_USER_FUNCTION) ?
 480                 closure->func.op_array.static_variables : NULL;
 481 }
 482 /* }}} */
 483 
 484 /* {{{ proto Closure::__construct()
 485    Private constructor preventing instantiation */
 486 ZEND_COLD ZEND_METHOD(Closure, __construct)
 487 {
 488         zend_throw_error(NULL, "Instantiation of 'Closure' is not allowed");
 489 }
 490 /* }}} */
 491 
 492 ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bindto, 0, 0, 1)
 493         ZEND_ARG_INFO(0, newthis)
 494         ZEND_ARG_INFO(0, newscope)
 495 ZEND_END_ARG_INFO()
 496 
 497 ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 2)
 498         ZEND_ARG_INFO(0, closure)
 499         ZEND_ARG_INFO(0, newthis)
 500         ZEND_ARG_INFO(0, newscope)
 501 ZEND_END_ARG_INFO()
 502 
 503 ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_call, 0, 0, 1)
 504         ZEND_ARG_INFO(0, newthis)
 505         ZEND_ARG_VARIADIC_INFO(0, parameters)
 506 ZEND_END_ARG_INFO()
 507 
 508 static const zend_function_entry closure_functions[] = {
 509         ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
 510         ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 511         ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, ZEND_ACC_PUBLIC)
 512         ZEND_ME(Closure, call, arginfo_closure_call, ZEND_ACC_PUBLIC)
 513         ZEND_FE_END
 514 };
 515 
 516 void zend_register_closure_ce(void) /* {{{ */
 517 {
 518         zend_class_entry ce;
 519 
 520         INIT_CLASS_ENTRY(ce, "Closure", closure_functions);
 521         zend_ce_closure = zend_register_internal_class(&ce);
 522         zend_ce_closure->ce_flags |= ZEND_ACC_FINAL;
 523         zend_ce_closure->create_object = zend_closure_new;
 524         zend_ce_closure->serialize = zend_class_serialize_deny;
 525         zend_ce_closure->unserialize = zend_class_unserialize_deny;
 526 
 527         memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
 528         closure_handlers.free_obj = zend_closure_free_storage;
 529         closure_handlers.clone_obj = NULL;
 530         closure_handlers.get_constructor = zend_closure_get_constructor;
 531         closure_handlers.get_method = zend_closure_get_method;
 532         closure_handlers.write_property = zend_closure_write_property;
 533         closure_handlers.read_property = zend_closure_read_property;
 534         closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
 535         closure_handlers.has_property = zend_closure_has_property;
 536         closure_handlers.unset_property = zend_closure_unset_property;
 537         closure_handlers.compare_objects = zend_closure_compare_objects;
 538         closure_handlers.clone_obj = zend_closure_clone;
 539         closure_handlers.get_debug_info = zend_closure_get_debug_info;
 540         closure_handlers.get_closure = zend_closure_get_closure;
 541         closure_handlers.get_gc = zend_closure_get_gc;
 542 }
 543 /* }}} */
 544 
 545 static void zend_closure_internal_handler(INTERNAL_FUNCTION_PARAMETERS) /* {{{ */
 546 {
 547         zend_closure *closure = (zend_closure*)EX(func)->common.prototype;
 548         closure->orig_internal_handler(INTERNAL_FUNCTION_PARAM_PASSTHRU);
 549         OBJ_RELEASE((zend_object*)closure);
 550         EX(func) = NULL;
 551 }
 552 /* }}} */
 553 
 554 ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
 555 {
 556         zend_closure *closure;
 557 
 558         object_init_ex(res, zend_ce_closure);
 559 
 560         closure = (zend_closure *)Z_OBJ_P(res);
 561 
 562         if ((scope == NULL) && this_ptr && (Z_TYPE_P(this_ptr) != IS_UNDEF)) {
 563                 /* use dummy scope if we're binding an object without specifying a scope */
 564                 /* maybe it would be better to create one for this purpose */
 565                 scope = zend_ce_closure;
 566         }
 567 
 568         if (func->type == ZEND_USER_FUNCTION) {
 569                 memcpy(&closure->func, func, sizeof(zend_op_array));
 570                 closure->func.common.prototype = (zend_function*)closure;
 571                 closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
 572                 if (closure->func.op_array.static_variables) {
 573                         HashTable *static_variables = closure->func.op_array.static_variables;
 574 
 575                         ALLOC_HASHTABLE(closure->func.op_array.static_variables);
 576                         zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
 577                         zend_hash_apply_with_arguments(static_variables, zval_copy_static_var, 1, closure->func.op_array.static_variables);
 578                 }
 579                 if (UNEXPECTED(!closure->func.op_array.run_time_cache)) {
 580                         closure->func.op_array.run_time_cache = func->op_array.run_time_cache = zend_arena_alloc(&CG(arena), func->op_array.cache_size);
 581                         memset(func->op_array.run_time_cache, 0, func->op_array.cache_size);
 582                 }
 583                 if (closure->func.op_array.refcount) {
 584                         (*closure->func.op_array.refcount)++;
 585                 }
 586         } else {
 587                 memcpy(&closure->func, func, sizeof(zend_internal_function));
 588                 closure->func.common.prototype = (zend_function*)closure;
 589                 closure->func.common.fn_flags |= ZEND_ACC_CLOSURE;
 590                 /* wrap internal function handler to avoid memory leak */
 591                 if (UNEXPECTED(closure->func.internal_function.handler == zend_closure_internal_handler)) {
 592                         /* avoid infinity recursion, by taking handler from nested closure */
 593                         zend_closure *nested = (zend_closure*)((char*)func - XtOffsetOf(zend_closure, func));
 594                         ZEND_ASSERT(nested->std.ce == zend_ce_closure);
 595                         closure->orig_internal_handler = nested->orig_internal_handler;
 596                 } else {
 597                         closure->orig_internal_handler = closure->func.internal_function.handler;
 598                 }
 599                 closure->func.internal_function.handler = zend_closure_internal_handler;
 600                 if (!func->common.scope) {
 601                         /* if it's a free function, we won't set scope & this since they're meaningless */
 602                         this_ptr = NULL;
 603                         scope = NULL;
 604                 }
 605         }
 606 
 607         ZVAL_UNDEF(&closure->this_ptr);
 608         /* Invariant:
 609          * If the closure is unscoped or static, it has no bound object. */
 610         closure->func.common.scope = scope;
 611         closure->called_scope = called_scope;
 612         if (scope) {
 613                 closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
 614                 if (this_ptr && Z_TYPE_P(this_ptr) == IS_OBJECT && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
 615                         ZVAL_COPY(&closure->this_ptr, this_ptr);
 616                 }
 617         }
 618 }
 619 /* }}} */
 620 
 621 ZEND_API void zend_create_fake_closure(zval *res, zend_function *func, zend_class_entry *scope, zend_class_entry *called_scope, zval *this_ptr) /* {{{ */
 622 {
 623         zend_closure *closure;
 624 
 625         zend_create_closure(res, func, scope, called_scope, this_ptr);
 626 
 627         closure = (zend_closure *)Z_OBJ_P(res);
 628         closure->func.common.fn_flags |= ZEND_ACC_FAKE_CLOSURE;
 629 }
 630 /* }}} */
 631 
 632 /*
 633  * Local variables:
 634  * tab-width: 4
 635  * c-basic-offset: 4
 636  * indent-tabs-mode: t
 637  * End:
 638  */

/* [<][>][^][v][top][bottom][index][help] */