Back to index

php5  5.3.10
zend_closures.c
Go to the documentation of this file.
00001 /*
00002    +----------------------------------------------------------------------+
00003    | Zend Engine                                                          |
00004    +----------------------------------------------------------------------+
00005    | Copyright (c) 1998-2012 Zend Technologies Ltd. (http://www.zend.com) |
00006    +----------------------------------------------------------------------+
00007    | This source file is subject to version 2.00 of the Zend license,     |
00008    | that is bundled with this package in the file LICENSE, and is        | 
00009    | available through the world-wide-web at the following url:           |
00010    | http://www.zend.com/license/2_00.txt.                                |
00011    | If you did not receive a copy of the Zend license and are unable to  |
00012    | obtain it through the world-wide-web, please send a note to          |
00013    | license@zend.com so we can mail you a copy immediately.              |
00014    +----------------------------------------------------------------------+
00015    | Authors: Christian Seiler <chris_se@gmx.net>                         |
00016    |          Dmitry Stogov <dmitry@zend.com>                             |
00017    |          Marcus Boerger <helly@php.net>                              |
00018    +----------------------------------------------------------------------+
00019 */
00020 
00021 /* $Id: zend_closures.c 321634 2012-01-01 13:15:04Z felipe $ */
00022 
00023 #include "zend.h"
00024 #include "zend_API.h"
00025 #include "zend_closures.h"
00026 #include "zend_exceptions.h"
00027 #include "zend_interfaces.h"
00028 #include "zend_objects.h"
00029 #include "zend_objects_API.h"
00030 #include "zend_globals.h"
00031 
00032 #define ZEND_CLOSURE_PRINT_NAME "Closure object"
00033 
00034 #define ZEND_CLOSURE_PROPERTY_ERROR() \
00035        zend_error(E_RECOVERABLE_ERROR, "Closure object cannot have properties")
00036 
00037 typedef struct _zend_closure {
00038        zend_object    std;
00039        zend_function  func;
00040        HashTable     *debug_info;
00041 } zend_closure;
00042 
00043 /* non-static since it needs to be referenced */
00044 ZEND_API zend_class_entry *zend_ce_closure;
00045 static zend_object_handlers closure_handlers;
00046 
00047 ZEND_METHOD(Closure, __invoke) /* {{{ */
00048 {
00049        zend_function *func = EG(current_execute_data)->function_state.function;
00050        zval ***arguments;
00051        zval *closure_result_ptr = NULL;
00052 
00053        arguments = emalloc(sizeof(zval**) * ZEND_NUM_ARGS());
00054        if (zend_get_parameters_array_ex(ZEND_NUM_ARGS(), arguments) == FAILURE) {
00055               efree(arguments);
00056               zend_error(E_RECOVERABLE_ERROR, "Cannot get arguments for calling closure");
00057               RETVAL_FALSE;
00058        } else if (call_user_function_ex(CG(function_table), NULL, this_ptr, &closure_result_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) {
00059               RETVAL_FALSE;
00060        } else if (closure_result_ptr) {
00061               if (Z_ISREF_P(closure_result_ptr) && return_value_ptr) {
00062                      if (return_value) {
00063                             zval_ptr_dtor(&return_value);
00064                      }
00065                      *return_value_ptr = closure_result_ptr;
00066               } else {
00067                      RETVAL_ZVAL(closure_result_ptr, 1, 1);
00068               }
00069        }
00070        efree(arguments);
00071 
00072        /* destruct the function also, then - we have allocated it in get_method */
00073        efree(func->internal_function.function_name);
00074        efree(func);
00075 }
00076 /* }}} */
00077 
00078 static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */
00079 {
00080        zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
00081        return NULL;
00082 }
00083 /* }}} */
00084 
00085 static int zend_closure_compare_objects(zval *o1, zval *o2 TSRMLS_DC) /* {{{ */
00086 {
00087        return (Z_OBJ_HANDLE_P(o1) != Z_OBJ_HANDLE_P(o2));
00088 }
00089 /* }}} */
00090 
00091 ZEND_API zend_function *zend_get_closure_invoke_method(zval *obj TSRMLS_DC) /* {{{ */
00092 {
00093        zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);       
00094        zend_function *invoke = (zend_function*)emalloc(sizeof(zend_function));
00095 
00096        invoke->common = closure->func.common;
00097        invoke->type = ZEND_INTERNAL_FUNCTION;
00098        invoke->internal_function.fn_flags = ZEND_ACC_PUBLIC | ZEND_ACC_CALL_VIA_HANDLER;
00099        invoke->internal_function.handler = ZEND_MN(Closure___invoke);
00100        invoke->internal_function.module = 0;
00101        invoke->internal_function.scope = zend_ce_closure;
00102        invoke->internal_function.function_name = estrndup(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1);
00103        return invoke;
00104 }
00105 /* }}} */
00106 
00107 ZEND_API const zend_function *zend_get_closure_method_def(zval *obj TSRMLS_DC) /* {{{ */
00108 {
00109        zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);       
00110        return &closure->func;
00111 }
00112 /* }}} */
00113 
00114 static zend_function *zend_closure_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
00115 {
00116        char *lc_name;
00117        ALLOCA_FLAG(use_heap)
00118 
00119        lc_name = do_alloca(method_len + 1, use_heap);
00120        zend_str_tolower_copy(lc_name, method_name, method_len);
00121        if ((method_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) &&
00122               memcmp(lc_name, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
00123        ) {
00124               free_alloca(lc_name, use_heap);
00125               return zend_get_closure_invoke_method(*object_ptr TSRMLS_CC);
00126        }
00127        free_alloca(lc_name, use_heap);
00128        return NULL;
00129 }
00130 /* }}} */
00131 
00132 static zval *zend_closure_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */
00133 {
00134        ZEND_CLOSURE_PROPERTY_ERROR();
00135        Z_ADDREF(EG(uninitialized_zval));
00136        return &EG(uninitialized_zval);
00137 }
00138 /* }}} */
00139 
00140 static void zend_closure_write_property(zval *object, zval *member, zval *value TSRMLS_DC) /* {{{ */
00141 {
00142        ZEND_CLOSURE_PROPERTY_ERROR();
00143 }
00144 /* }}} */
00145 
00146 static zval **zend_closure_get_property_ptr_ptr(zval *object, zval *member TSRMLS_DC) /* {{{ */
00147 {
00148        ZEND_CLOSURE_PROPERTY_ERROR();
00149        return NULL;
00150 }
00151 /* }}} */
00152 
00153 static int zend_closure_has_property(zval *object, zval *member, int has_set_exists TSRMLS_DC) /* {{{ */
00154 {
00155        if (has_set_exists != 2) {
00156               ZEND_CLOSURE_PROPERTY_ERROR();
00157        }
00158        return 0;
00159 }
00160 /* }}} */
00161 
00162 static void zend_closure_unset_property(zval *object, zval *member TSRMLS_DC) /* {{{ */
00163 {
00164        ZEND_CLOSURE_PROPERTY_ERROR();
00165 }
00166 /* }}} */
00167 
00168 static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */
00169 {
00170        zend_closure *closure = (zend_closure *)object;
00171 
00172        zend_object_std_dtor(&closure->std TSRMLS_CC);
00173 
00174        if (closure->func.type == ZEND_USER_FUNCTION) {
00175               zend_execute_data *ex = EG(current_execute_data);
00176               while (ex) {
00177                      if (ex->op_array == &closure->func.op_array) {
00178                             zend_error(E_ERROR, "Cannot destroy active lambda function");
00179                      }
00180                      ex = ex->prev_execute_data;
00181               }
00182               destroy_op_array(&closure->func.op_array TSRMLS_CC);
00183        }
00184 
00185        if (closure->debug_info != NULL) {
00186               zend_hash_destroy(closure->debug_info);
00187               efree(closure->debug_info);
00188        }
00189 
00190        efree(closure);
00191 }
00192 /* }}} */
00193 
00194 static zend_object_value zend_closure_new(zend_class_entry *class_type TSRMLS_DC) /* {{{ */
00195 {
00196        zend_closure *closure;
00197        zend_object_value object;
00198 
00199        closure = emalloc(sizeof(zend_closure));
00200        memset(closure, 0, sizeof(zend_closure));
00201 
00202        zend_object_std_init(&closure->std, class_type TSRMLS_CC);
00203 
00204        object.handle = zend_objects_store_put(closure, (zend_objects_store_dtor_t)zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_closure_free_storage, NULL TSRMLS_CC);
00205        object.handlers = &closure_handlers;
00206 
00207        return object;
00208 }
00209 /* }}} */
00210 
00211 int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
00212 {
00213        zend_closure *closure;
00214 
00215        if (Z_TYPE_P(obj) != IS_OBJECT) {
00216               return FAILURE;
00217        }
00218 
00219        closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
00220        *fptr_ptr = &closure->func;
00221 
00222        if (zobj_ptr) {
00223               *zobj_ptr = NULL;
00224        }
00225        *ce_ptr = NULL;
00226        return SUCCESS;
00227 }
00228 /* }}} */
00229 
00230 static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
00231 {
00232        zend_closure *closure = (zend_closure *)zend_object_store_get_object(object TSRMLS_CC);
00233        zval *val;
00234        struct _zend_arg_info *arg_info = closure->func.common.arg_info;
00235 
00236        *is_temp = 0;
00237 
00238        if (closure->debug_info == NULL) {
00239               ALLOC_HASHTABLE(closure->debug_info);
00240               zend_hash_init(closure->debug_info, 1, NULL, ZVAL_PTR_DTOR, 0);
00241        }
00242        if (closure->debug_info->nApplyCount == 0) {
00243               if (closure->func.type == ZEND_USER_FUNCTION && closure->func.op_array.static_variables) {
00244                      HashTable *static_variables = closure->func.op_array.static_variables;
00245                      MAKE_STD_ZVAL(val);
00246                      array_init(val);
00247                      zend_hash_copy(Z_ARRVAL_P(val), static_variables, (copy_ctor_func_t)zval_add_ref, NULL, sizeof(zval*));
00248                      zend_symtable_update(closure->debug_info, "static", sizeof("static"), (void *) &val, sizeof(zval *), NULL);
00249               }
00250 
00251               if (arg_info) {
00252                      zend_uint i, required = closure->func.common.required_num_args;
00253 
00254                      MAKE_STD_ZVAL(val);
00255                      array_init(val);
00256 
00257                      for (i = 0; i < closure->func.common.num_args; i++) {
00258                             char *name, *info;
00259                             int name_len, info_len;
00260                             if (arg_info->name) {
00261                                    name_len = zend_spprintf(&name, 0, "%s$%s",
00262                                                                arg_info->pass_by_reference ? "&" : "",
00263                                                                arg_info->name);
00264                             } else {
00265                                    name_len = zend_spprintf(&name, 0, "%s$param%d",
00266                                                                arg_info->pass_by_reference ? "&" : "",
00267                                                                i + 1);
00268                             }
00269                             info_len = zend_spprintf(&info, 0, "%s",
00270                                                         i >= required ? "<optional>" : "<required>");
00271                             add_assoc_stringl_ex(val, name, name_len + 1, info, info_len, 0);
00272                             efree(name);
00273                             arg_info++;
00274                      }
00275                      zend_symtable_update(closure->debug_info, "parameter", sizeof("parameter"), (void *) &val, sizeof(zval *), NULL);
00276               }
00277        }
00278 
00279        return closure->debug_info;
00280 }
00281 /* }}} */
00282 
00283 static HashTable *zend_closure_get_properties(zval *obj TSRMLS_DC) /* {{{ */
00284 {
00285        zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);       
00286 
00287        if (GC_G(gc_active)) {
00288               return (closure->func.type == ZEND_USER_FUNCTION) ? closure->func.op_array.static_variables : NULL;
00289        }
00290 
00291        return closure->std.properties;
00292 }
00293 /* }}} */
00294 
00295 /* {{{ proto Closure::__construct()
00296    Private constructor preventing instantiation */
00297 ZEND_METHOD(Closure, __construct)
00298 {
00299        zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
00300 }
00301 /* }}} */
00302 
00303 static const zend_function_entry closure_functions[] = {
00304        ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
00305        {NULL, NULL, NULL}
00306 };
00307 
00308 void zend_register_closure_ce(TSRMLS_D) /* {{{ */
00309 {
00310        zend_class_entry ce;
00311 
00312        INIT_CLASS_ENTRY(ce, "Closure", closure_functions);
00313        zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC);
00314        zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS;
00315        zend_ce_closure->create_object = zend_closure_new;
00316        zend_ce_closure->serialize = zend_class_serialize_deny;
00317        zend_ce_closure->unserialize = zend_class_unserialize_deny;
00318 
00319        memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
00320        closure_handlers.get_constructor = zend_closure_get_constructor;
00321        closure_handlers.get_method = zend_closure_get_method;
00322        closure_handlers.write_property = zend_closure_write_property;
00323        closure_handlers.read_property = zend_closure_read_property;
00324        closure_handlers.get_property_ptr_ptr = zend_closure_get_property_ptr_ptr;
00325        closure_handlers.has_property = zend_closure_has_property;
00326        closure_handlers.unset_property = zend_closure_unset_property;
00327        closure_handlers.compare_objects = zend_closure_compare_objects;
00328        closure_handlers.clone_obj = NULL;
00329        closure_handlers.get_debug_info = zend_closure_get_debug_info;
00330        closure_handlers.get_closure = zend_closure_get_closure;
00331        closure_handlers.get_properties = zend_closure_get_properties;
00332 }
00333 /* }}} */
00334 
00335 static int zval_copy_static_var(zval **p TSRMLS_DC, int num_args, va_list args, zend_hash_key *key) /* {{{ */
00336 {
00337        HashTable *target = va_arg(args, HashTable*);
00338        zend_bool is_ref;
00339        zval *tmp;
00340 
00341        if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) {
00342               is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF;
00343 
00344               if (!EG(active_symbol_table)) {
00345                      zend_rebuild_symbol_table(TSRMLS_C);
00346               }
00347               if (zend_hash_quick_find(EG(active_symbol_table), key->arKey, key->nKeyLength, key->h, (void **) &p) == FAILURE) {
00348                      if (is_ref) {
00349                             ALLOC_INIT_ZVAL(tmp);
00350                             Z_SET_ISREF_P(tmp);
00351                             zend_hash_quick_add(EG(active_symbol_table), key->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), (void**)&p);
00352                      } else {
00353                             tmp = EG(uninitialized_zval_ptr);
00354                             zend_error(E_NOTICE,"Undefined variable: %s", key->arKey);
00355                      }
00356               } else {
00357                      if (is_ref) {
00358                             SEPARATE_ZVAL_TO_MAKE_IS_REF(p);
00359                             tmp = *p;
00360                      } else if (Z_ISREF_PP(p)) {
00361                             ALLOC_INIT_ZVAL(tmp);
00362                             *tmp = **p;
00363                             zval_copy_ctor(tmp);
00364                             Z_SET_REFCOUNT_P(tmp, 0);
00365                             Z_UNSET_ISREF_P(tmp);
00366                      } else {
00367                             tmp = *p;
00368                      }
00369               }
00370        } else {
00371               tmp = *p;
00372        }
00373        if (zend_hash_quick_add(target, key->arKey, key->nKeyLength, key->h, &tmp, sizeof(zval*), NULL) == SUCCESS) {
00374               Z_ADDREF_P(tmp);
00375        }
00376        return ZEND_HASH_APPLY_KEEP;
00377 }
00378 /* }}} */
00379 
00380 ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC) /* {{{ */
00381 {
00382        zend_closure *closure;
00383 
00384        object_init_ex(res, zend_ce_closure);
00385 
00386        closure = (zend_closure *)zend_object_store_get_object(res TSRMLS_CC);
00387 
00388        closure->func = *func;
00389        closure->func.common.prototype = NULL;
00390 
00391        if (closure->func.type == ZEND_USER_FUNCTION) {
00392               if (closure->func.op_array.static_variables) {
00393                      HashTable *static_variables = closure->func.op_array.static_variables;
00394 
00395                      ALLOC_HASHTABLE(closure->func.op_array.static_variables);
00396                      zend_hash_init(closure->func.op_array.static_variables, zend_hash_num_elements(static_variables), NULL, ZVAL_PTR_DTOR, 0);
00397                      zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables);
00398               }
00399               (*closure->func.op_array.refcount)++;
00400        }
00401 
00402        closure->func.common.scope = NULL;
00403 }
00404 /* }}} */
00405 
00406 /*
00407  * Local variables:
00408  * tab-width: 4
00409  * c-basic-offset: 4
00410  * indent-tabs-mode: t
00411  * End:
00412  */