PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - Zend - zend_closures.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 314 335 93.7 %
Date: 2016-07-19 Functions: 25 30 83.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Wed, 20 Jul 2016 02:56:15 +0000 (4 days ago)

Copyright © 2005-2016 The PHP Group
All rights reserved.