From: Dmitry Stogov Date: Tue, 8 Jul 2008 16:15:08 +0000 (+0000) Subject: Closure::__invoke() is fixed to support references X-Git-Tag: BEFORE_HEAD_NS_CHANGE~1342 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=36ec3480cf53a552af4d9a7fa888e782668c1f49;p=php Closure::__invoke() is fixed to support references --- diff --git a/Zend/tests/closure_013.phpt b/Zend/tests/closure_013.phpt new file mode 100644 index 0000000000..66a72d6ba3 --- /dev/null +++ b/Zend/tests/closure_013.phpt @@ -0,0 +1,29 @@ +--TEST-- +Closure 013: __invoke() on temporary result +--SKIPIF-- + +--FILE-- +__invoke(); +$test = foo(); +$test->__invoke(); +$test = foo()->__invoke(); +?> +--EXPECT-- +Hello World! +Hello World! +Hello World! diff --git a/Zend/tests/closure_014.phpt b/Zend/tests/closure_014.phpt new file mode 100644 index 0000000000..3a68652f7a --- /dev/null +++ b/Zend/tests/closure_014.phpt @@ -0,0 +1,83 @@ +--TEST-- +Closure 014: return by value/reference +--SKIPIF-- + +--FILE-- +__invoke()); +$x(); +$x->__invoke(); +$x = function() { + return 0; +}; +var_dump($x()); +var_dump($x->__invoke()); +$x(); +$x->__invoke(); + +$x = new C2(); +$a = $b = $c = $d = 1; +$e =& $x($a); +$e = 2; +var_dump($a); +$e =& $x->__invoke($b); +$e = 3; +var_dump($b); +$x($b); +$x->__invoke($b); +$x = function & (&$a) { + return $a; +}; +$e =& $x($c); +$e = 4; +var_dump($c); +$e =& $x->__invoke($d); +$e = 5; +var_dump($d); +$x($d); +$x->__invoke($d); + +$x = new C3(); +var_dump($x()); +var_dump($x->__invoke()); +$x(); +$x->__invoke(); +$x = function() { +}; +var_dump($x()); +var_dump($x->__invoke()); +$x(); +$x->__invoke(); +?> +--EXPECT-- +int(0) +int(0) +int(0) +int(0) +int(2) +int(3) +int(4) +int(5) +NULL +NULL +NULL +NULL diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 3a7f48e84e..ff67d7e502 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -26,10 +26,13 @@ #include "zend_objects_API.h" #include "zend_globals.h" +#define ZEND_INVOKE_FUNC_NAME "__invoke" + typedef struct _zend_closure { zend_object std; zend_function func; zval *this_ptr; + zend_function *invoke; } zend_closure; static zend_class_entry *zend_ce_closure; @@ -47,28 +50,25 @@ ZEND_METHOD(Closure, __invoke) /* {{{ */ RETURN_FALSE; } - if (!return_value_ptr) { - return_value_ptr = &closure_result_ptr; - } - - if (call_user_function_ex(CG(function_table), NULL, this_ptr, return_value_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) { + if (call_user_function_ex(CG(function_table), NULL, this_ptr, &closure_result_ptr, ZEND_NUM_ARGS(), arguments, 1, NULL TSRMLS_CC) == FAILURE) { efree(arguments); RETURN_FALSE; } efree(arguments); if (closure_result_ptr) { - RETVAL_ZVAL(closure_result_ptr, 1, 1); + if (Z_ISREF_P(closure_result_ptr) && return_value_ptr) { + if (return_value) { + zval_ptr_dtor(&return_value); + } + *return_value_ptr = closure_result_ptr; + } else { + RETVAL_ZVAL(closure_result_ptr, 1, 1); + } } } /* }}} */ -const static zend_function_entry closure_functions[] = { /* {{{ */ - ZEND_ME(Closure, __invoke, NULL, 0) - {NULL, NULL, NULL} -}; -/* }}} */ - static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */ { zend_error(E_ERROR, "Instantiation of 'Closure' is not allowed"); @@ -103,6 +103,39 @@ static int zend_closure_cast_object_tostring(zval *readobj, zval *writeobj, int } /* }}} */ +static zend_function *zend_closure_get_method(zval **object_ptr, zstr method_name, int method_len TSRMLS_DC) /* {{{ */ +{ + unsigned int lc_name_len; + zstr lc_name; + zend_uchar type = UG(unicode)?IS_UNICODE:IS_STRING; + + /* Create a zend_copy_str_tolower(dest, src, src_length); */ + lc_name = zend_u_str_case_fold(type, method_name, method_len, 1, &lc_name_len); + if ((lc_name_len == sizeof(ZEND_INVOKE_FUNC_NAME)-1) && + (ZEND_U_EQUAL(type, lc_name, lc_name_len, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1))) { + zend_closure *closure = (zend_closure *)zend_object_store_get_object(*object_ptr TSRMLS_CC); + + if (!closure->invoke) { + closure->invoke = (zend_function*)emalloc(sizeof(zend_function)); + closure->invoke->common = closure->func.common; + closure->invoke->type = ZEND_INTERNAL_FUNCTION; + closure->invoke->internal_function.handler = ZEND_MN(Closure___invoke); + closure->invoke->internal_function.module = 0; + closure->invoke->internal_function.scope = zend_ce_closure; + if (UG(unicode)) { + closure->invoke->internal_function.function_name.u = USTR_MAKE(ZEND_INVOKE_FUNC_NAME); + } else { + closure->invoke->internal_function.function_name.s = ZEND_INVOKE_FUNC_NAME; + } + } + efree(lc_name.v); + return (zend_function *)closure->invoke; + } + efree(lc_name.v); + return NULL; +} +/* }}} */ + static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */ { zend_closure *closure = (zend_closure *)object; @@ -124,6 +157,13 @@ static void zend_closure_free_storage(void *object TSRMLS_DC) /* {{{ */ zval_ptr_dtor(&closure->this_ptr); } + if (closure->invoke) { + if (UG(unicode)) { + efree(closure->invoke->internal_function.function_name.u); + } + efree(closure->invoke); + } + efree(closure); } /* }}} */ @@ -149,13 +189,14 @@ void zend_register_closure_ce(TSRMLS_D) /* {{{ */ { zend_class_entry ce; - INIT_CLASS_ENTRY(ce, "Closure", closure_functions); + INIT_CLASS_ENTRY(ce, "Closure", NULL); zend_ce_closure = zend_register_internal_class(&ce TSRMLS_CC); zend_ce_closure->ce_flags |= ZEND_ACC_FINAL_CLASS; zend_ce_closure->create_object = zend_closure_new; memcpy(&closure_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); closure_handlers.get_constructor = zend_closure_get_constructor; + closure_handlers.get_method = zend_closure_get_method; closure_handlers.compare_objects = zend_closure_compare_objects; closure_handlers.cast_object = zend_closure_cast_object_tostring; closure_handlers.clone_obj = NULL; @@ -169,7 +210,6 @@ static int zval_copy_static_var(zval **p, int num_args, va_list args, zend_hash_ TSRMLS_FETCH(); if (Z_TYPE_PP(p) & (IS_LEXICAL_VAR|IS_LEXICAL_REF)) { - TSRMLS_FETCH(); is_ref = Z_TYPE_PP(p) & IS_LEXICAL_REF; if (!EG(active_symbol_table)) { @@ -243,9 +283,9 @@ ZEND_API int zend_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_functio zend_uchar utype = UG(unicode)?IS_UNICODE:IS_STRING; if (utype == IS_UNICODE) { - key.u = USTR_MAKE("__invoke"); + key.u = USTR_MAKE(ZEND_INVOKE_FUNC_NAME); } else { - key.s = "__invoke"; + key.s = ZEND_INVOKE_FUNC_NAME; } if (Z_TYPE_P(obj) == IS_OBJECT) { @@ -276,7 +316,7 @@ ZEND_API int zend_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_functio efree(key.u); } return SUCCESS; - } else if (zend_u_hash_find(&ce->function_table, utype, key, sizeof("__invoke"), (void**)fptr_ptr) == SUCCESS) { + } else if (zend_u_hash_find(&ce->function_table, utype, key, sizeof(ZEND_INVOKE_FUNC_NAME), (void**)fptr_ptr) == SUCCESS) { *ce_ptr = ce; if ((*fptr_ptr)->common.fn_flags & ZEND_ACC_STATIC) { if (zobj_ptr) {