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(¤t_op->op2.u.constant); ZVAL_LONG(¤t_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