summaryrefslogtreecommitdiff
path: root/Zend/zend_closures.c
diff options
context:
space:
mode:
Diffstat (limited to 'Zend/zend_closures.c')
-rw-r--r--Zend/zend_closures.c86
1 files changed, 72 insertions, 14 deletions
diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c
index c10c1a4327..c8497a4f2d 100644
--- a/Zend/zend_closures.c
+++ b/Zend/zend_closures.c
@@ -61,11 +61,66 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */
efree(arguments);
/* destruct the function also, then - we have allocated it in get_method */
- STR_RELEASE(func->internal_function.function_name);
+ zend_string_release(func->internal_function.function_name);
efree(func);
}
/* }}} */
+/* {{{ proto mixed Closure::call(object $to [, mixed $parameter] [, mixed $...] )
+ Call closure, binding to a given object with its class as the scope */
+ZEND_METHOD(Closure, call)
+{
+ zval *zclosure, *newthis, closure_result;
+ zend_closure *closure;
+ zend_fcall_info fci;
+ zend_fcall_info_cache fci_cache;
+ zval *my_params;
+ int my_param_count = 0;
+ zend_function my_function;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o*", &newthis, &my_params, &my_param_count) == FAILURE) {
+ return;
+ }
+
+ zclosure = getThis();
+ closure = (zend_closure *)Z_OBJ_P(zclosure);
+
+ if (closure->func.common.fn_flags & ZEND_ACC_STATIC) {
+ zend_error(E_WARNING, "Cannot bind an instance to a static closure");
+ return;
+ }
+
+ if (closure->func.type == ZEND_INTERNAL_FUNCTION) {
+ /* verify that we aren't binding internal function to a wrong object */
+ if ((closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0 &&
+ !instanceof_function(Z_OBJCE_P(newthis), closure->func.common.scope TSRMLS_CC)) {
+ zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", closure->func.common.scope->name->val, closure->func.common.function_name->val, Z_OBJCE_P(newthis)->name->val);
+ return;
+ }
+ }
+
+ /* This should never happen as closures will always be callable */
+ if (zend_fcall_info_init(zclosure, 0, &fci, &fci_cache, NULL, NULL TSRMLS_CC) != SUCCESS) {
+ ZEND_ASSERT(0);
+ }
+
+ fci.retval = &closure_result;
+ fci.params = my_params;
+ fci.param_count = my_param_count;
+ fci.object = fci_cache.object = Z_OBJ_P(newthis);
+ fci_cache.initialized = 1;
+
+ my_function = *fci_cache.function_handler;
+ /* use scope of passed object */
+ my_function.common.scope = Z_OBJCE_P(newthis);
+ fci_cache.function_handler = &my_function;
+
+ if (zend_call_function(&fci, &fci_cache TSRMLS_CC) == SUCCESS && Z_TYPE(closure_result) != IS_UNDEF) {
+ ZVAL_COPY_VALUE(return_value, &closure_result);
+ }
+}
+/* }}} */
+
/* {{{ proto Closure Closure::bind(Closure $old, object $to [, mixed $scope = "static" ] )
Create a closure from another one and bind to another object and scope */
ZEND_METHOD(Closure, bind)
@@ -91,15 +146,14 @@ ZEND_METHOD(Closure, bind)
ce = NULL;
} else {
zend_string *class_name = zval_get_string(scope_arg);
- if ((class_name->len == sizeof("static") - 1) &&
- (memcmp("static", class_name->val, sizeof("static") - 1) == 0)) {
+ if (zend_string_equals_literal(class_name, "static")) {
ce = closure->func.common.scope;
} else if ((ce = zend_lookup_class_ex(class_name, NULL, 1 TSRMLS_CC)) == NULL) {
zend_error(E_WARNING, "Class '%s' not found", class_name->val);
- STR_RELEASE(class_name);
+ zend_string_release(class_name);
RETURN_NULL();
}
- STR_RELEASE(class_name);
+ zend_string_release(class_name);
}
} else { /* scope argument not given; do not change the scope by default */
ce = closure->func.common.scope;
@@ -133,7 +187,7 @@ ZEND_API zend_function *zend_get_closure_invoke_method(zend_object *object TSRML
invoke->internal_function.handler = ZEND_MN(Closure___invoke);
invoke->internal_function.module = 0;
invoke->internal_function.scope = zend_ce_closure;
- invoke->internal_function.function_name = STR_INIT(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0);
+ invoke->internal_function.function_name = zend_string_init(ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1, 0);
return invoke;
}
/* }}} */
@@ -156,15 +210,13 @@ static zend_function *zend_closure_get_method(zend_object **object, zend_string
{
zend_string *lc_name;
- lc_name = STR_ALLOC(method->len, 0);
+ lc_name = zend_string_alloc(method->len, 0);
zend_str_tolower_copy(lc_name->val, method->val, method->len);
- if ((method->len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) &&
- memcmp(lc_name->val, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
- ) {
- STR_FREE(lc_name);
+ if (zend_string_equals_literal(method, ZEND_INVOKE_FUNC_NAME)) {
+ zend_string_free(lc_name);
return zend_get_closure_invoke_method(*object TSRMLS_CC);
}
- STR_FREE(lc_name);
+ zend_string_free(lc_name);
return std_object_handlers.get_method(object, method, key TSRMLS_CC);
}
/* }}} */
@@ -308,7 +360,7 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_
}
if (arg_info) {
- zend_uint i, required = closure->func.common.required_num_args;
+ uint32_t i, required = closure->func.common.required_num_args;
array_init(&val);
@@ -326,7 +378,7 @@ static HashTable *zend_closure_get_debug_info(zval *object, int *is_temp TSRMLS_
}
ZVAL_STR(&info, zend_strpprintf(0, "%s", i >= required ? "<optional>" : "<required>"));
zend_hash_update(Z_ARRVAL(val), name, &info);
- STR_RELEASE(name);
+ zend_string_release(name);
arg_info++;
}
zend_hash_str_update(closure->debug_info, "parameter", sizeof("parameter")-1, &val);
@@ -373,10 +425,16 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 2)
ZEND_ARG_INFO(0, newscope)
ZEND_END_ARG_INFO()
+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_call, 0, 0, 1)
+ ZEND_ARG_INFO(0, newthis)
+ ZEND_ARG_VARIADIC_INFO(0, parameters)
+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)
{NULL, NULL, NULL}
};