]> granicus.if.org Git - php/commitdiff
Closure::__invoke() is fixed to support references
authorDmitry Stogov <dmitry@php.net>
Tue, 8 Jul 2008 16:15:08 +0000 (16:15 +0000)
committerDmitry Stogov <dmitry@php.net>
Tue, 8 Jul 2008 16:15:08 +0000 (16:15 +0000)
Zend/tests/closure_013.phpt [new file with mode: 0644]
Zend/tests/closure_014.phpt [new file with mode: 0644]
Zend/zend_closures.c

diff --git a/Zend/tests/closure_013.phpt b/Zend/tests/closure_013.phpt
new file mode 100644 (file)
index 0000000..66a72d6
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+Closure 013: __invoke() on temporary result
+--SKIPIF--
+<?php 
+       if (!class_exists('Closure')) die('skip Closure support is needed');
+?>
+--FILE--
+<?php
+class Foo {
+       function __invoke() {
+               echo "Hello World!\n";
+       }
+}
+
+function foo() {
+       return function() {
+               echo "Hello World!\n";
+       };
+}
+$test = new Foo;
+$test->__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 (file)
index 0000000..3a68652
--- /dev/null
@@ -0,0 +1,83 @@
+--TEST--
+Closure 014: return by value/reference
+--SKIPIF--
+<?php 
+       if (!class_exists('Closure')) die('skip Closure support is needed');
+?>
+--FILE--
+<?php
+class C1 {
+       function __invoke() {
+               return 0;
+       }
+}
+class C2 {
+       function &__invoke(&$a) {
+               return $a;
+       }
+}
+class C3 {
+       function __invoke() {
+       }
+}
+
+$x = new C1();
+var_dump($x());
+var_dump($x->__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
index 3a7f48e84ea6420b009bdb6644dbec603133d395..ff67d7e502e90458e11d126266009e82dc2d0c43 100644 (file)
 #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) {