diff options
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r-- | Zend/zend_closures.c | 104 |
1 files changed, 104 insertions, 0 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 810ac6a66c..dadf461d71 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -235,6 +235,105 @@ ZEND_METHOD(Closure, bind) } /* }}} */ +static void zend_closure_call_magic(INTERNAL_FUNCTION_PARAMETERS) { + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + memset(&fci, 0, sizeof(zend_fcall_info)); + memset(&fci, 0, sizeof(zend_fcall_info_cache)); + + fci.size = sizeof(zend_fcall_info); + fci.retval = return_value; + + fcc.initialized = 1; + fcc.function_handler = (zend_function *) EX(func)->common.arg_info; + fci.params = (zval*) emalloc(sizeof(zval) * 2); + fci.param_count = 2; + ZVAL_STR(&fci.params[0], EX(func)->common.function_name); + array_init(&fci.params[1]); + zend_copy_parameters_array(ZEND_NUM_ARGS(), &fci.params[1]); + + fci.object = Z_OBJ(EX(This)); + fcc.object = Z_OBJ(EX(This)); + fcc.calling_scope = zend_get_executed_scope(); + + zend_call_function(&fci, &fcc); + + zval_ptr_dtor(&fci.params[0]); + zval_ptr_dtor(&fci.params[1]); + efree(fci.params); +} + + +static int zend_create_closure_from_callable(zval *return_value, zval *callable, char **error) { + zend_fcall_info_cache fcc; + zend_function *mptr; + zval instance; + + if (!zend_is_callable_ex(callable, NULL, 0, NULL, &fcc, error)) { + return FAILURE; + } + + mptr = fcc.function_handler; + if (mptr == NULL) { + return FAILURE; + } + + if (mptr->common.fn_flags & ZEND_ACC_CALL_VIA_TRAMPOLINE) { + zend_internal_function call; + memset(&call, 0, sizeof(zend_internal_function)); + + call.type = ZEND_INTERNAL_FUNCTION; + call.handler = zend_closure_call_magic; + call.function_name = mptr->common.function_name; + call.arg_info = (zend_internal_arg_info *) mptr->common.prototype; + call.scope = mptr->common.scope; + + zend_free_trampoline(mptr); + mptr = (zend_function *) &call; + } + + ZVAL_OBJ(&instance, fcc.object); + zend_create_closure(return_value, mptr, mptr->common.scope, fcc.object ? fcc.object->ce : NULL, fcc.object ? &instance : NULL); + + return SUCCESS; +} + + +/* {{{ proto Closure Closure::fromCallable(callable callable) + Create a closure from a callable using the current scope. */ +ZEND_METHOD(Closure, fromCallable) +{ + zval *callable; + int success; + char *error = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &callable) == FAILURE) { + return; + } + + if (Z_TYPE_P(callable) == IS_OBJECT && instanceof_function(Z_OBJCE_P(callable), zend_ce_closure)) { + // It's already a closure + RETURN_ZVAL(callable, 1, 0); + } + + // create closure as if it were called from parent scope + EG(current_execute_data) = EX(prev_execute_data); + success = zend_create_closure_from_callable(return_value, callable, &error); + EG(current_execute_data) = execute_data; + + if (success == FAILURE) { + zend_clear_exception(); + if (error) { + zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable: %s", error); + efree(error); + } else { + zend_throw_exception_ex(zend_ce_type_error, 0, "Failed to create closure from callable"); + } + } +} +/* }}} */ + static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object) /* {{{ */ { zend_throw_error(NULL, "Instantiation of 'Closure' is not allowed"); @@ -489,11 +588,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_call, 0, 0, 1) ZEND_ARG_VARIADIC_INFO(0, parameters) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_fromcallable, 0, 0, 1) + ZEND_ARG_INFO(0, callable) +ZEND_END_ARG_INFO() + static const zend_function_entry closure_functions[] = { ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE) ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_MALIAS(Closure, bindTo, bind, arginfo_closure_bindto, ZEND_ACC_PUBLIC) ZEND_ME(Closure, call, arginfo_closure_call, ZEND_ACC_PUBLIC) + ZEND_ME(Closure, fromCallable, arginfo_closure_fromcallable, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) ZEND_FE_END }; |