stas                                     Mon, 19 Apr 2010 19:45:03 +0000

Revision: http://svn.php.net/viewvc?view=revision&revision=298187

Log:
restore $this support for closures to its former glory

Changed paths:
    U   php/php-src/trunk/NEWS
    A   php/php-src/trunk/Zend/tests/closure_005.phpt
    A   php/php-src/trunk/Zend/tests/closure_007.phpt
    U   php/php-src/trunk/Zend/tests/closure_020.phpt
    U   php/php-src/trunk/Zend/tests/closure_024.phpt
    U   php/php-src/trunk/Zend/tests/closure_026.phpt
    A   php/php-src/trunk/Zend/tests/closure_036.phpt
    U   php/php-src/trunk/Zend/zend_closures.c
    U   php/php-src/trunk/Zend/zend_closures.h
    U   php/php-src/trunk/Zend/zend_compile.c
    U   php/php-src/trunk/Zend/zend_compile.h
    U   php/php-src/trunk/Zend/zend_language_parser.y
    U   php/php-src/trunk/Zend/zend_vm_def.h
    U   php/php-src/trunk/Zend/zend_vm_execute.h
    U   php/php-src/trunk/ext/reflection/php_reflection.c
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosureThis.phpt
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_basic.phpt
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_error.phpt
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_basic.phpt
    A   
php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_error.phpt
    A   php/php-src/trunk/ext/reflection/tests/closures_003_v1.phpt
    A   php/php-src/trunk/ext/reflection/tests/closures_004.phpt

Modified: php/php-src/trunk/NEWS
===================================================================
--- php/php-src/trunk/NEWS	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/NEWS	2010-04-19 19:45:03 UTC (rev 298187)
@@ -11,6 +11,7 @@
   ReflectionExtension::isPersistent(). (Johannes)
 - Added ReflectionZendExtension class. (Johannes)
 - Added command line option --rz to CLI. (Johannes)
+- Added closure $this support back. (Stas)

 - default_charset if not specified is now UTF-8 instead of ISO-8859-1. (Rasmus)


Added: php/php-src/trunk/Zend/tests/closure_005.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_005.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/closure_005.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,74 @@
+--TEST--
+Closure 005: Lambda inside class, lifetime of $this
+--FILE--
+<?php
+
+class A {
+	private $x;
+
+	function __construct($x) {
+		$this->x = $x;
+	}
+
+	function __destruct() {
+		echo "Destroyed\n";
+	}
+
+	function getIncer($val) {
+		return function() use ($val) {
+			$this->x += $val;
+		};
+	}
+
+	function getPrinter() {
+		return function() {
+			echo $this->x."\n";
+		};
+	}
+
+	function getError() {
+		return static function() {
+			echo $this->x."\n";
+		};
+	}
+
+	function printX() {
+		echo $this->x."\n";
+	}
+}
+
+$a = new A(3);
+$incer = $a->getIncer(2);
+$printer = $a->getPrinter();
+$error = $a->getError();
+
+$a->printX();
+$printer();
+$incer();
+$a->printX();
+$printer();
+
+unset($a);
+
+$incer();
+$printer();
+
+unset($incer);
+$printer();
+
+unset($printer);
+
+$error();
+
+echo "Done\n";
+?>
+--EXPECTF--
+3
+3
+5
+5
+7
+7
+Destroyed
+
+Fatal error: Using $this when not in object context in %sclosure_005.php on line 28

Added: php/php-src/trunk/Zend/tests/closure_007.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_007.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/closure_007.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,38 @@
+--TEST--
+Closure 007: Nested lambdas in classes
+--FILE--
+<?php
+
+class A {
+	private $x = 0;
+
+	function getClosureGetter () {
+		return function () {
+			return function () {
+				$this->x++;
+			};
+		};
+	}
+
+	function printX () {
+		echo $this->x."\n";
+	}
+}
+
+$a = new A;
+$a->printX();
+$getClosure = $a->getClosureGetter();
+$a->printX();
+$closure = $getClosure();
+$a->printX();
+$closure();
+$a->printX();
+
+echo "Done\n";
+?>
+--EXPECT--
+0
+0
+0
+1
+Done

Modified: php/php-src/trunk/Zend/tests/closure_020.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_020.phpt	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/tests/closure_020.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -23,16 +23,18 @@

 ?>
 --EXPECTF--
-object(foo)#%d (%d) {
+object(foo)#%d (2) {
   ["test":"foo":private]=>
   int(3)
   ["a"]=>
-  object(Closure)#%d (1) {
+  object(Closure)#%d (2) {
     ["static"]=>
     array(1) {
       ["a"]=>
       *RECURSION*
     }
+    ["this"]=>
+    *RECURSION*
   }
 }
 bool(true)

Modified: php/php-src/trunk/Zend/tests/closure_024.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_024.phpt	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/tests/closure_024.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -1,16 +1,26 @@
 --TEST--
-Closure 024: Trying to clone the Closure object
+Closure 024: Clone the Closure object
 --FILE--
 <?php

-$a = function () {
-	return clone function () {
-		return 1;
-	};
-};
+$a = 1;
+$c = function($add) use(&$a) { return $a+$add; };

-$a();
+$cc = clone $c;

+echo $c(10)."\n";
+echo $cc(10)."\n";
+
+$a++;
+
+echo $c(10)."\n";
+echo $cc(10)."\n";
+
+echo "Done.\n";
 ?>
 --EXPECTF--
-Fatal error: Trying to clone an uncloneable object of class Closure in %s on line %d
+11
+11
+12
+12
+Done.
\ No newline at end of file

Modified: php/php-src/trunk/Zend/tests/closure_026.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_026.phpt	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/tests/closure_026.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -32,7 +32,9 @@
   ["a"]=>
   array(1) {
     [0]=>
-    object(Closure)#%d (0) {
+    object(Closure)#%d (1) {
+      ["this"]=>
+      *RECURSION*
     }
   }
 }
@@ -41,7 +43,12 @@
 string(1) "a"
 array(1) {
   [0]=>
-  object(Closure)#%d (0) {
+  object(Closure)#%d (1) {
+    ["this"]=>
+    object(foo)#%d (1) {
+      ["a"]=>
+      *RECURSION*
+    }
   }
 }
 int(1)

Added: php/php-src/trunk/Zend/tests/closure_036.phpt
===================================================================
--- php/php-src/trunk/Zend/tests/closure_036.phpt	                        (rev 0)
+++ php/php-src/trunk/Zend/tests/closure_036.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,33 @@
+--TEST--
+Closure 036: Rebinding closures
+--FILE--
+<?php
+
+class A {
+	private $x;
+
+	public function __construct($v) {
+		$this->x = $v;
+	}
+
+	public function getIncrementor() {
+		return function() { return ++$this->x; };
+	}
+}
+
+$a = new A(0);
+$b = new A(10);
+
+$ca = $a->getIncrementor();
+$cb = $ca->bindTo($b);
+$cb2 = Closure::bind($b, $ca);
+
+var_dump($ca());
+var_dump($cb());
+var_dump($cb2());
+
+?>
+--EXPECTF--
+int(1)
+int(11)
+int(12)
\ No newline at end of file


Property changes on: php/php-src/trunk/Zend/tests/closure_036.phpt
___________________________________________________________________
Added: svn:executable
   + *

Modified: php/php-src/trunk/Zend/zend_closures.c
===================================================================
--- php/php-src/trunk/Zend/zend_closures.c	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_closures.c	2010-04-19 19:45:03 UTC (rev 298187)
@@ -37,6 +37,7 @@
 typedef struct _zend_closure {
 	zend_object    std;
 	zend_function  func;
+	zval          *this_ptr;
 	HashTable     *debug_info;
 } zend_closure;

@@ -75,6 +76,39 @@
 }
 /* }}} */

+/* {{{ proto Closure Closure::bindTo(object $to)
+   Bind a closure to another object */
+ZEND_METHOD(Closure, bindTo) /* {{{ */
+{
+	zval *newthis;
+	zend_closure *closure = (zend_closure *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!", &newthis) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	zend_create_closure(return_value, &closure->func, newthis?Z_OBJCE_P(newthis):NULL, newthis TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ proto Closure Closure::bind(object $to, Closure $old)
+   Create a closure to with binding to another object */
+ZEND_METHOD(Closure, bind) /* {{{ */
+{
+	zval *newthis, *zclosure;
+	zend_closure *closure;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o!O", &newthis, &zclosure, zend_ce_closure) == FAILURE) {
+		RETURN_NULL();
+	}
+
+	closure = (zend_closure *)zend_object_store_get_object(zclosure TSRMLS_CC);
+
+	zend_create_closure(return_value, &closure->func, newthis?Z_OBJCE_P(newthis):NULL, newthis TSRMLS_CC);
+}
+/* }}} */
+
+
 static zend_function *zend_closure_get_constructor(zval *object TSRMLS_DC) /* {{{ */
 {
 	zend_error(E_RECOVERABLE_ERROR, "Instantiation of 'Closure' is not allowed");
@@ -111,6 +145,13 @@
 }
 /* }}} */

+ZEND_API zval* zend_get_closure_this_ptr(zval *obj TSRMLS_DC) /* {{{ */
+{
+	zend_closure *closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
+	return closure->this_ptr;
+}
+/* }}} */
+
 static zend_function *zend_closure_get_method(zval **object_ptr, char *method_name, int method_len TSRMLS_DC) /* {{{ */
 {
 	char *lc_name;
@@ -125,7 +166,7 @@
 		return zend_get_closure_invoke_method(*object_ptr TSRMLS_CC);
 	}
 	free_alloca(lc_name, use_heap);
-	return NULL;
+	return std_object_handlers.get_method(object_ptr, method_name, method_len TSRMLS_CC);
 }
 /* }}} */

@@ -187,6 +228,10 @@
 		efree(closure->debug_info);
 	}

+	if (closure->this_ptr) {
+		zval_ptr_dtor(&closure->this_ptr);
+	}
+
 	efree(closure);
 }
 /* }}} */
@@ -208,6 +253,17 @@
 }
 /* }}} */

+static zend_object_value zend_closure_clone(zval *zobject TSRMLS_DC) /* {{{ */
+{
+	zend_closure *closure = (zend_closure *)zend_object_store_get_object(zobject TSRMLS_CC);
+	zval result;
+
+	zend_create_closure(&result, &closure->func, closure->func.common.scope, closure->this_ptr);
+	return Z_OBJVAL(result);
+}
+/* }}} */
+
+
 int zend_closure_get_closure(zval *obj, zend_class_entry **ce_ptr, zend_function **fptr_ptr, zval **zobj_ptr TSRMLS_DC) /* {{{ */
 {
 	zend_closure *closure;
@@ -219,10 +275,17 @@
 	closure = (zend_closure *)zend_object_store_get_object(obj TSRMLS_CC);
 	*fptr_ptr = &closure->func;

-	if (zobj_ptr) {
-		*zobj_ptr = NULL;
+	if (closure->this_ptr) {
+		if (zobj_ptr) {
+			*zobj_ptr = closure->this_ptr;
+		}
+		*ce_ptr = Z_OBJCE_P(closure->this_ptr);
+	} else {
+		if (zobj_ptr) {
+			*zobj_ptr = NULL;
+		}
+		*ce_ptr = closure->func.common.scope;
 	}
-	*ce_ptr = NULL;
 	return SUCCESS;
 }
 /* }}} */
@@ -248,6 +311,11 @@
 			zend_symtable_update(closure->debug_info, "static", sizeof("static"), (void *) &val, sizeof(zval *), NULL);
 		}

+		if (closure->this_ptr) {
+			Z_ADDREF_P(closure->this_ptr);
+			zend_symtable_update(closure->debug_info, "this", sizeof("this"), (void *) &closure->this_ptr, sizeof(zval *), NULL);
+		}
+
 		if (arg_info) {
 			zend_uint i, required = closure->func.common.required_num_args;

@@ -288,8 +356,19 @@
 }
 /* }}} */

+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bindto, 0, 0, 0)
+	ZEND_ARG_INFO(0, newthis)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_closure_bind, 0, 0, 0)
+	ZEND_ARG_INFO(0, newthis)
+	ZEND_ARG_INFO(0, closure)
+ZEND_END_ARG_INFO()
+
 static const zend_function_entry closure_functions[] = {
 	ZEND_ME(Closure, __construct, NULL, ZEND_ACC_PRIVATE)
+	ZEND_ME(Closure, bindTo, arginfo_closure_bindto, ZEND_ACC_PUBLIC)
+	ZEND_ME(Closure, bind, arginfo_closure_bind, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
 	{NULL, NULL, NULL}
 };

@@ -313,7 +392,7 @@
 	closure_handlers.has_property = zend_closure_has_property;
 	closure_handlers.unset_property = zend_closure_unset_property;
 	closure_handlers.compare_objects = zend_closure_compare_objects;
-	closure_handlers.clone_obj = NULL;
+	closure_handlers.clone_obj = zend_closure_clone;
 	closure_handlers.get_debug_info = zend_closure_get_debug_info;
 	closure_handlers.get_closure = zend_closure_get_closure;
 }
@@ -356,7 +435,7 @@
 }
 /* }}} */

-ZEND_API void zend_create_closure(zval *res, zend_function *func TSRMLS_DC) /* {{{ */
+ZEND_API void zend_create_closure(zval *res, zend_function *func, zend_class_entry *scope, zval *this_ptr TSRMLS_DC) /* {{{ */
 {
 	zend_closure *closure;

@@ -375,9 +454,39 @@
 			zend_hash_apply_with_arguments(static_variables TSRMLS_CC, (apply_func_args_t)zval_copy_static_var, 1, closure->func.op_array.static_variables);
 		}
 		(*closure->func.op_array.refcount)++;
+	} else {
+		/* verify that we aren't binding internal function to a wrong scope */
+		if(func->common.scope != NULL) {
+			if(scope && !instanceof_function(scope, func->common.scope TSRMLS_CC)) {
+				zend_error(E_WARNING, "Cannot bind function %s::%s to scope class %s", func->common.scope->name, func->common.function_name, scope->name);
+				scope = NULL;
+			}
+			if(scope && this_ptr && (func->common.fn_flags & ZEND_ACC_STATIC) == 0 &&
+					!instanceof_function(Z_OBJCE_P(this_ptr), closure->func.common.scope TSRMLS_CC)) {
+				zend_error(E_WARNING, "Cannot bind function %s::%s to object of class %s", func->common.scope->name, func->common.function_name, Z_OBJCE_P(this_ptr)->name);
+				scope = NULL;
+				this_ptr = NULL;
+			}
+		} else {
+			/* if it's a free function, we won't set scope & this since they're meaningless */
+			this_ptr = NULL;
+			scope = NULL;
+		}
 	}

-	closure->func.common.scope = NULL;
+	closure->func.common.scope = scope;
+	if (scope) {
+		closure->func.common.fn_flags |= ZEND_ACC_PUBLIC;
+		if (this_ptr && (closure->func.common.fn_flags & ZEND_ACC_STATIC) == 0) {
+			closure->this_ptr = this_ptr;
+			Z_ADDREF_P(this_ptr);
+		} else {
+			closure->func.common.fn_flags |= ZEND_ACC_STATIC;
+			closure->this_ptr = NULL;
+		}
+	} else {
+		closure->this_ptr = NULL;
+	}
 }
 /* }}} */


Modified: php/php-src/trunk/Zend/zend_closures.h
===================================================================
--- php/php-src/trunk/Zend/zend_closures.h	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_closures.h	2010-04-19 19:45:03 UTC (rev 298187)
@@ -30,9 +30,10 @@

 extern ZEND_API zend_class_entry *zend_ce_closure;

-ZEND_API void zend_create_closure(zval *res, zend_function *op_array TSRMLS_DC);
+ZEND_API void zend_create_closure(zval *res, zend_function *op_array, zend_class_entry *scope, zval *this_ptr TSRMLS_DC);
 ZEND_API zend_function *zend_get_closure_invoke_method(zval *obj TSRMLS_DC);
 ZEND_API const zend_function *zend_get_closure_method_def(zval *obj TSRMLS_DC);
+ZEND_API zval* zend_get_closure_this_ptr(zval *obj TSRMLS_DC);

 END_EXTERN_C()


Modified: php/php-src/trunk/Zend/zend_compile.c
===================================================================
--- php/php-src/trunk/Zend/zend_compile.c	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_compile.c	2010-04-19 19:45:03 UTC (rev 298187)
@@ -1403,7 +1403,7 @@
 }
 /* }}} */

-void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference TSRMLS_DC) /* {{{ */
+void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC) /* {{{ */
 {
 	znode          function_name;
 	zend_op_array *current_op_array = CG(active_op_array);
@@ -1423,6 +1423,9 @@
 	zval_dtor(&current_op->op2.u.constant);
 	ZVAL_LONG(&current_op->op2.u.constant, zend_hash_func(Z_STRVAL(current_op->op1.u.constant), Z_STRLEN(current_op->op1.u.constant)));
 	current_op->result = *result;
+	if (is_static) {
+	    CG(active_op_array)->fn_flags |= ZEND_ACC_STATIC;
+	}
 	CG(active_op_array)->fn_flags |= ZEND_ACC_CLOSURE;
 }
 /* }}} */

Modified: php/php-src/trunk/Zend/zend_compile.h
===================================================================
--- php/php-src/trunk/Zend/zend_compile.h	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_compile.h	2010-04-19 19:45:03 UTC (rev 298187)
@@ -434,7 +434,7 @@
 void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC);
 void zend_do_handle_exception(TSRMLS_D);

-void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference TSRMLS_DC);
+void zend_do_begin_lambda_function_declaration(znode *result, znode *function_token, int return_reference, int is_static TSRMLS_DC);
 void zend_do_fetch_lexical_variable(znode *varname, zend_bool is_ref TSRMLS_DC);

 void zend_do_try(znode *try_token TSRMLS_DC);

Modified: php/php-src/trunk/Zend/zend_language_parser.y
===================================================================
--- php/php-src/trunk/Zend/zend_language_parser.y	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_language_parser.y	2010-04-19 19:45:03 UTC (rev 298187)
@@ -647,8 +647,10 @@
 	|	T_ARRAY '(' array_pair_list ')' { $$ = $3; }
 	|	'`' backticks_expr '`' { zend_do_shell_exec(&$$, &$2 TSRMLS_CC); }
 	|	T_PRINT expr  { zend_do_print(&$$, &$2 TSRMLS_CC); }
-	|	function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type TSRMLS_CC); }
+	|	function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$1, $2.op_type, 0 TSRMLS_CC); }
 			parameter_list ')' lexical_vars '{' inner_statement_list '}' {  zend_do_end_function_declaration(&$1 TSRMLS_CC); $$ = $4; }
+	|	T_STATIC function is_reference '(' { zend_do_begin_lambda_function_declaration(&$$, &$2, $3.op_type, 1 TSRMLS_CC); }
+			parameter_list ')' lexical_vars '{' inner_statement_list '}' {  zend_do_end_function_declaration(&$2 TSRMLS_CC); $$ = $5; }
 ;

 function:

Modified: php/php-src/trunk/Zend/zend_vm_def.h
===================================================================
--- php/php-src/trunk/Zend/zend_vm_def.h	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_vm_def.h	2010-04-19 19:45:03 UTC (rev 298187)
@@ -4414,7 +4414,7 @@
 		zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
 	}

-	zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array TSRMLS_CC);
+	zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array, EG(scope), EG(This) TSRMLS_CC);

 	ZEND_VM_NEXT_OPCODE();
 }

Modified: php/php-src/trunk/Zend/zend_vm_execute.h
===================================================================
--- php/php-src/trunk/Zend/zend_vm_execute.h	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/Zend/zend_vm_execute.h	2010-04-19 19:45:03 UTC (rev 298187)
@@ -2947,7 +2947,7 @@
 		zend_error_noreturn(E_ERROR, "Base lambda function for closure not found");
 	}

-	zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array TSRMLS_CC);
+	zend_create_closure(&EX_T(opline->result.u.var).tmp_var, op_array, EG(scope), EG(This) TSRMLS_CC);

 	ZEND_VM_NEXT_OPCODE();
 }

Modified: php/php-src/trunk/ext/reflection/php_reflection.c
===================================================================
--- php/php-src/trunk/ext/reflection/php_reflection.c	2010-04-19 18:43:33 UTC (rev 298186)
+++ php/php-src/trunk/ext/reflection/php_reflection.c	2010-04-19 19:45:03 UTC (rev 298187)
@@ -323,7 +323,7 @@
 }

 static void _const_string(string *str, char *name, zval *value, char *indent TSRMLS_DC);
-static void _function_string(string *str, zend_function *fptr, zend_class_entry *scope, char *indent TSRMLS_DC);
+static void _function_string(string *str, zend_function *fptr, zend_class_entry *scope, char* indent TSRMLS_DC);
 static void _property_string(string *str, zend_property_info *prop, char *prop_name, char* indent TSRMLS_DC);
 static void _class_string(string *str, zend_class_entry *ce, zval *obj, char *indent TSRMLS_DC);
 static void _extension_string(string *str, zend_module_entry *module, char *indent TSRMLS_DC);
@@ -1608,6 +1608,44 @@
 }
 /* }}} */

+/* {{{ proto public bool ReflectionFunction::getClosureThis()
+   Returns this pointer bound to closure */
+ZEND_METHOD(reflection_function, getClosureThis)
+{
+	reflection_object *intern;
+	zend_function *fptr;
+	zval* closure_this;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+	GET_REFLECTION_OBJECT_PTR(fptr);
+	if (intern->obj) {
+		closure_this = zend_get_closure_this_ptr(intern->obj TSRMLS_CC);
+		if (closure_this) {
+			RETURN_ZVAL(closure_this, 1, 0);
+		}
+	}
+}
+/* }}} */
+
+/* {{{ proto public mixed ReflectionFunction::getClosure()
+   Returns a dynamically created closure for the function */
+ZEND_METHOD(reflection_function, getClosure)
+{
+	reflection_object *intern;
+	zend_function *fptr;
+
+	if (zend_parse_parameters_none() == FAILURE) {
+		return;
+	}
+	GET_REFLECTION_OBJECT_PTR(fptr);
+
+	zend_create_closure(return_value, fptr, NULL, NULL TSRMLS_CC);
+}
+/* }}} */
+
+
 /* {{{ proto public bool ReflectionFunction::isInternal()
    Returns whether this is an internal function */
 ZEND_METHOD(reflection_function, isInternal)
@@ -2066,8 +2104,8 @@
 					&& memcmp(lcname, ZEND_INVOKE_FUNC_NAME, sizeof(ZEND_INVOKE_FUNC_NAME)-1) == 0
 					&& (fptr = zend_get_closure_invoke_method(*classref TSRMLS_CC)) != NULL)
 				{
-					/* nothign to do. don't set is_closure since is the invoke handler,
-					   not the closure itself */
+					/* nothing to do. don't set is_closure since is the invoke handler,
+-					   not the closure itself */
 				} else if (zend_hash_find(&ce->function_table, lcname, lcname_len + 1, (void **) &fptr) == FAILURE) {
 					efree(lcname);
 					zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC,
@@ -2559,6 +2597,41 @@
 }
 /* }}} */

+/* {{{ proto public mixed ReflectionMethod::getClosure([mixed object])
+   Invokes the function */
+ZEND_METHOD(reflection_method, getClosure)
+{
+	reflection_object *intern;
+	zval *obj;
+	zend_function *mptr;
+
+	METHOD_NOTSTATIC(reflection_method_ptr);
+	GET_REFLECTION_OBJECT_PTR(mptr);
+
+	if (mptr->common.fn_flags & ZEND_ACC_STATIC)  {
+		zend_create_closure(return_value, mptr, mptr->common.scope, NULL TSRMLS_CC);
+	} else {
+		if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &obj) == FAILURE) {
+			return;
+		}
+
+		if (!instanceof_function(Z_OBJCE_P(obj), mptr->common.scope TSRMLS_CC)) {
+			_DO_THROW("Given object is not an instance of the class this method was declared in");
+			/* Returns from this function */
+		}
+
+		/* This is an original closure object and __invoke is to be called. */
+		if (Z_OBJCE_P(obj) == zend_ce_closure && mptr->type == ZEND_INTERNAL_FUNCTION &&
+			(mptr->internal_function.fn_flags & ZEND_ACC_CALL_VIA_HANDLER) != 0)
+		{
+			RETURN_ZVAL(obj, 1, 0);
+		} else {
+			zend_create_closure(return_value, mptr, mptr->common.scope, obj TSRMLS_CC);
+		}
+	}
+}
+/* }}} */
+
 /* {{{ proto public mixed ReflectionMethod::invoke(mixed object, mixed* args)
    Invokes the method. */
 ZEND_METHOD(reflection_method, invoke)
@@ -5261,6 +5334,7 @@
 	ZEND_ME(reflection_function, isDeprecated, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, isInternal, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, isUserDefined, arginfo_reflection__void, 0)
+	ZEND_ME(reflection_function, getClosureThis, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, getDocComment, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, getEndLine, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, getExtension, arginfo_reflection__void, 0)
@@ -5285,6 +5359,7 @@
 	ZEND_ME(reflection_function, isDisabled, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_function, invoke, arginfo_reflection_function_invoke, 0)
 	ZEND_ME(reflection_function, invokeArgs, arginfo_reflection_function_invokeArgs, 0)
+	ZEND_ME(reflection_function, getClosure, arginfo_reflection__void, 0)
 	{NULL, NULL, NULL}
 };

@@ -5313,6 +5388,10 @@
 	ZEND_ARG_INFO(0, value)
 ZEND_END_ARG_INFO()

+ZEND_BEGIN_ARG_INFO(arginfo_reflection_method_getClosure, 0)
+	ZEND_ARG_INFO(0, object)
+ZEND_END_ARG_INFO()
+
 static const zend_function_entry reflection_method_functions[] = {
 	ZEND_ME(reflection_method, export, arginfo_reflection_method_export, ZEND_ACC_STATIC|ZEND_ACC_PUBLIC)
 	ZEND_ME(reflection_method, __construct, arginfo_reflection_method___construct, 0)
@@ -5325,6 +5404,7 @@
 	ZEND_ME(reflection_method, isStatic, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_method, isConstructor, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_method, isDestructor, arginfo_reflection__void, 0)
+	ZEND_ME(reflection_method, getClosure, arginfo_reflection_method_getClosure, 0)
 	ZEND_ME(reflection_method, getModifiers, arginfo_reflection__void, 0)
 	ZEND_ME(reflection_method, invoke, arginfo_reflection_method_invoke, 0)
 	ZEND_ME(reflection_method, invokeArgs, arginfo_reflection_method_invokeArgs, 0)

Added: php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosureThis.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosureThis.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosureThis.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,17 @@
+--TEST--
+Reflection::getClosureThis()
+--SKIPIF--
+<?php
+if (!extension_loaded('reflection') || !defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+  print 'skip';
+}
+?>
+--FILE--
+<?php
+$closure = function($param) { return "this is a closure"; };
+$rf = new ReflectionFunction($closure);
+var_dump($rf->getClosureThis());
+echo "Done!\n";
+--EXPECTF--
+NULL
+Done!


Property changes on: php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosureThis.phpt
___________________________________________________________________
Added: svn:executable
   + *

Added: php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_basic.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_basic.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_basic.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,37 @@
+--TEST--
+Test ReflectionFunction::getClosure() function : basic functionality
+--FILE--
+<?php
+/* Prototype  : public mixed ReflectionFunction::getClosure()
+ * Description: Returns a dynamically created closure for the function
+ * Source code: ext/reflection/php_reflection.c
+ * Alias to functions:
+ */
+
+echo "*** Testing ReflectionFunction::getClosure() : basic functionality ***\n";
+
+function foo()
+{
+	var_dump( "Inside foo function" );
+}
+
+function bar( $arg )
+{
+	var_dump( "Arg is " . $arg );
+}
+
+$func = new ReflectionFunction( 'foo' );
+$closure = $func->getClosure();
+$closure();
+
+$func = new ReflectionFunction( 'bar' );
+$closure = $func->getClosure();
+$closure( 'succeeded' );
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing ReflectionFunction::getClosure() : basic functionality ***
+string(19) "Inside foo function"
+string(16) "Arg is succeeded"
+===DONE===

Added: php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_error.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_error.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionFunction_getClosure_error.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,27 @@
+--TEST--
+Test ReflectionFunction::getClosure() function : error functionality
+--FILE--
+<?php
+/* Prototype  : public mixed ReflectionFunction::getClosure()
+ * Description: Returns a dynamically created closure for the function
+ * Source code: ext/reflection/php_reflection.c
+ * Alias to functions:
+ */
+
+echo "*** Testing ReflectionFunction::getClosure() : error conditions ***\n";
+
+function foo()
+{
+	var_dump( "Inside foo function" );
+}
+
+$func = new ReflectionFunction( 'foo' );
+$closure = $func->getClosure('bar');
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing ReflectionFunction::getClosure() : error conditions ***
+
+Warning: ReflectionFunction::getClosure() expects exactly 0 parameters, 1 given in %s on line %d
+===DONE===

Added: php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,53 @@
+--TEST--
+Reflection::getClosureThis()
+--SKIPIF--
+<?php
+if (!extension_loaded('reflection') || !defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50300) {
+  print 'skip';
+}
+?>
+--FILE--
+<?php
+class StaticExample
+{
+	static function foo()
+	{
+		var_dump( "Static Example class, Hello World!" );
+	}
+}
+
+class Example
+{
+	public $bar = 42;
+	public function foo()
+	{
+		var_dump( "Example class, bar: " . $this->bar );
+	}
+}
+
+// Initialize classes
+$class = new ReflectionClass( 'Example' );
+$staticclass = new ReflectionClass( 'StaticExample' );
+$object = new Example();
+
+$method = $staticclass->getMethod( 'foo' );
+$closure = $method->getClosure();
+$rf = new ReflectionFunction($closure);
+
+var_dump($rf->getClosureThis());
+
+$method = $class->getMethod( 'foo' );
+
+$closure = $method->getClosure( $object );
+$rf = new ReflectionFunction($closure);
+
+var_dump($rf->getClosureThis());
+
+echo "Done!\n";
+--EXPECTF--
+NULL
+object(Example)#%d (1) {
+  ["bar"]=>
+  int(42)
+}
+Done!


Property changes on: php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosureThis.phpt
___________________________________________________________________
Added: svn:executable
   + *

Added: php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_basic.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_basic.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_basic.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,55 @@
+--TEST--
+Test ReflectionMethod::getClosure() function : basic functionality
+--FILE--
+<?php
+/* Prototype  : public mixed ReflectionFunction::getClosure()
+ * Description: Returns a dynamically created closure for the method
+ * Source code: ext/reflection/php_reflection.c
+ * Alias to functions:
+ */
+
+echo "*** Testing ReflectionMethod::getClosure() : basic functionality ***\n";
+
+class StaticExample
+{
+	static function foo()
+	{
+		var_dump( "Static Example class, Hello World!" );
+	}
+}
+
+class Example
+{
+	public $bar = 42;
+	public function foo()
+	{
+		var_dump( "Example class, bar: " . $this->bar );
+	}
+}
+
+// Initialize classes
+$class = new ReflectionClass( 'Example' );
+$staticclass = new ReflectionClass( 'StaticExample' );
+$object = new Example();
+$fakeobj = new StdClass();
+
+
+$method = $staticclass->getMethod( 'foo' );
+$closure = $method->getClosure();
+$closure();
+
+$method = $class->getMethod( 'foo' );
+
+$closure = $method->getClosure( $object );
+$closure();
+$object->bar = 34;
+$closure();
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing ReflectionMethod::getClosure() : basic functionality ***
+string(34) "Static Example class, Hello World!"
+string(22) "Example class, bar: 42"
+string(22) "Example class, bar: 34"
+===DONE===

Added: php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_error.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_error.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/ReflectionMethod_getClosure_error.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,73 @@
+--TEST--
+Test ReflectionMethod::getClosure() function : error functionality
+--FILE--
+<?php
+/* Prototype  : public mixed ReflectionFunction::getClosure()
+ * Description: Returns a dynamically created closure for the method
+ * Source code: ext/reflection/php_reflection.c
+ * Alias to functions:
+ */
+
+echo "*** Testing ReflectionMethod::getClosure() : error conditions ***\n";
+
+class StaticExample
+{
+	static function foo()
+	{
+		var_dump( "Static Example class, Hello World!" );
+	}
+}
+
+class Example
+{
+	public $bar = 42;
+	public function foo()
+	{
+		var_dump( "Example class, bar: " . $this->bar );
+	}
+}
+
+// Initialize classes
+$class = new ReflectionClass( 'Example' );
+$staticclass = new ReflectionClass( 'StaticExample' );
+$method = $class->getMethod( 'foo' );
+$staticmethod = $staticclass->getMethod( 'foo' );
+$object = new Example();
+$fakeobj = new StdClass();
+
+echo "\n-- Testing ReflectionMethod::getClosure() function with more than expected no. of arguments --\n";
+var_dump( $staticmethod->getClosure( 'foobar' ) );
+var_dump( $staticmethod->getClosure( 'foo', 'bar' ) );
+var_dump( $method->getClosure( $object, 'foobar' ) );
+
+echo "\n-- Testing ReflectionMethod::getClosure() function with Zero arguments --\n";
+$closure = $method->getClosure();
+
+echo "\n-- Testing ReflectionMethod::getClosure() function with Zero arguments --\n";
+try {
+        var_dump( $method->getClosure( $fakeobj ) );
+} catch( Exception $e ) {
+        var_dump( $e->getMessage() );
+}
+
+?>
+===DONE===
+--EXPECTF--
+*** Testing ReflectionMethod::getClosure() : error conditions ***
+
+-- Testing ReflectionMethod::getClosure() function with more than expected no. of arguments --
+object(Closure)#%d (0) {
+}
+object(Closure)#%d (0) {
+}
+
+Warning: ReflectionMethod::getClosure() expects exactly 1 parameter, 2 given in %s on line %d
+NULL
+
+-- Testing ReflectionMethod::getClosure() function with Zero arguments --
+
+Warning: ReflectionMethod::getClosure() expects exactly 1 parameter, 0 given in %s on line %d
+
+-- Testing ReflectionMethod::getClosure() function with Zero arguments --
+string(72) "Given object is not an instance of the class this method was declared in"
+===DONE===

Added: php/php-src/trunk/ext/reflection/tests/closures_003_v1.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/closures_003_v1.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/closures_003_v1.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,25 @@
+--TEST--
+Reflection on closures: Segfaults with getParameters() and getDeclaringFunction()
+--FILE--
+<?php
+
+$closure = function($a, $b = 0) { };
+
+$method = new ReflectionFunction ($closure);
+$params = $method->getParameters ();
+unset ($method);
+$method = $params[0]->getDeclaringFunction ();
+unset ($params);
+echo $method->getName ()."\n";
+
+$parameter = new ReflectionParameter ($closure, 'b');
+$method = $parameter->getDeclaringFunction ();
+unset ($parameter);
+echo $method->getName ()."\n";
+
+?>
+===DONE===
+--EXPECTF--
+{closure}
+{closure}
+===DONE===


Property changes on: php/php-src/trunk/ext/reflection/tests/closures_003_v1.phpt
___________________________________________________________________
Added: svn:executable
   + *

Added: php/php-src/trunk/ext/reflection/tests/closures_004.phpt
===================================================================
--- php/php-src/trunk/ext/reflection/tests/closures_004.phpt	                        (rev 0)
+++ php/php-src/trunk/ext/reflection/tests/closures_004.phpt	2010-04-19 19:45:03 UTC (rev 298187)
@@ -0,0 +1,43 @@
+--TEST--
+Reflection on closures: Segfault with getClosure() on closure itself
+--FILE--
+<?php
+$closure = function() { echo "Invoked!\n"; };
+
+$method = new ReflectionFunction ($closure);
+
+$closure2 = $method->getClosure ();
+
+$closure2 ();
+$closure2->__invoke ();
+
+unset ($closure);
+
+$closure2 ();
+$closure2->__invoke ();
+
+$closure = function() { echo "Invoked!\n"; };
+
+$method = new ReflectionMethod ($closure, '__invoke');
+$closure2 = $method->getClosure ($closure);
+
+$closure2 ();
+$closure2->__invoke ();
+
+unset ($closure);
+
+$closure2 ();
+$closure2->__invoke ();
+
+?>
+===DONE===
+--EXPECTF--
+Invoked!
+Invoked!
+Invoked!
+Invoked!
+Invoked!
+Invoked!
+Invoked!
+Invoked!
+===DONE===
-- 
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to