Re: [RFC] Re: [PHP-DEV] __invoke() weirdness
| From: | Marcus Boerger | Date: | Thu, 01 Jan 2009 16:50:39 +0000 |
| Subject: | Re: [RFC] Re: [PHP-DEV] __invoke() weirdness | ||
| References: | 1 2 3 4 5 6 7 8 | Groups: | php.internals |
| Request: | Send a blank email to internals+get-42439@lists.php.net to get a copy of this message | ||
Hello David,
I added test closure_037.phpt to demonstrate this.
marcus
Thursday, January 1, 2009, 5:23:08 PM, you wrote:
> Hi folks,
> first of all, thank you Marcus for implementing this. Very cool.
> As for the __get()/__getClosure() stuff, I don't think it's necessary
> or even an issue. One can never simply do $this-
>>getOverloadPropertyWithInvoke() anyway, because if the prop is not
> there, a fatal error would be the result. An __isset() call has to be
> made first along with an (instanceof Closure ||
> method_exists('__invoke')) check in userspace code.
> Which brings me to the next question - will
> $method = 'something';
> $bar->$method();
> work? Not sure if it's necessary; just curious for the most part.
> - David
> On 01.01.2009, at 17:06, Marcus Boerger wrote:
>> Hello Hannes,
>>
>> as discussed online. At the moment we should not have any __get()
>> calls during method resolution. The newly updated patch does that
>> now. And I think we are now safe to submit.
>>
>> In the future we could think of adding __getClosure() which would be
>> called to resolve a dynamic closure. But for the moment we do not need
>> it badly and the patch with the increased consistency is good enough.
>>
>> marcus
>>
>> Thursday, January 1, 2009, 4:09:39 PM, you wrote:
>>
>>> Hello Hannes,
>>
>>> Wednesday, December 31, 2008, 8:33:43 PM, you wrote:
>>
>>>> On Wed, Dec 31, 2008 at 20:12, Marcus Boerger <helly@php.net> wrote:
>>>>> Hello Lars,
>>>>>
>>>>> Wednesday, December 31, 2008, 6:59:08 PM, you wrote:
>>>>>
>>>>>> Hi Markus,
>>>>>
>>>>>> have you measured the performance impact in a class with - say -
>>>>>> ten
>>>>>> methods? And what to do with __get() and __call()? How are the
>>>>>> prioritized in the method resolve order?
>>>>>
>>>>> Translated into user code we now have:
>>>>>
>>>>> public function __zend_call($name, $args) {
>>>>> // Added property lookup
>>>>> if (isset($this->$name)) { // may call __isset
>>>>> $callable = $this->$name; // may call __get
>>
>>>> Uhmm. I hope I got this wrong as:
>>
>>> Well yes, there are no __isset() calls unless you call isset() of
>>> course.
>>
>>> I have updated the patch and added a test to demonstrate it better
>>> (closure_036.phpt). I also added debug information to closures
>>> which makes
>>> development much easier. The next step is to fix an issue in the
>>> engine and
>>> then submit unless there is a bigger issue with this.
>>
>>>> class foo {
>>>> function __isset() {
>>>> return true;
>>>> }
>>>> function __get() {
>>>> return "hello world";
>>>> }
>>>> function __call() {
>>>> }
>>>> }
>>
>>>> $foo = new foo;
>>>> $foo->foobar();
>>
>>>> will first execute __isset(), then __get() and then __call()? That
>>>> is
>>>> a major backwards compatibility break, and increases the
>>>> inconsistency
>>>> and decreases readability 10times
>>
>>>> -Hannes
>>
>>
>>
>>
>>> Best regards,
>>> Marcus
>>
>>
>>
>> Best regards,
>> Marcus<ze2-callable-properties-5.3-20090101-b.diff.txt>--
>> PHP Internals - PHP Runtime Development Mailing List
>> To unsubscribe, visit: http://www.php.net/unsub.php
Best regards,
Marcus
Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.135.2.6.2.22.2.23 diff -u -p -d -r1.135.2.6.2.22.2.23 zend_object_handlers.c --- Zend/zend_object_handlers.c 31 Dec 2008 11:15:32 -0000 1.135.2.6.2.22.2.23 +++ Zend/zend_object_handlers.c 1 Jan 2009 16:48:22 -0000 @@ -791,6 +791,24 @@ static union _zend_function *zend_std_ge zobj = Z_OBJ_P(object); if (zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&fbc) == FAILURE) { + zval **callable, member; + zend_property_info *property_info; + + ZVAL_STRINGL(&member, method_name, method_len, 0); + property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC); + + if (property_info && zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &callable) == SUCCESS) { + zval *callable_obj; + zend_class_entry *ce_ptr; + + if (Z_TYPE_PP(callable) == IS_OBJECT + && Z_OBJ_HANDLER_PP(callable, get_closure) + && Z_OBJ_HANDLER_PP(callable, get_closure)(*callable, &ce_ptr, &fbc, &callable_obj TSRMLS_CC) == SUCCESS) { + *object_ptr = callable_obj; + free_alloca(lc_method_name, use_heap); + return fbc; + } + } free_alloca(lc_method_name, use_heap); if (zobj->ce->__call) { zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); Index: Zend/tests/closure_033.phpt =================================================================== RCS file: Zend/tests/closure_033.phpt diff -N Zend/tests/closure_033.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_033.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,122 @@ +--TEST-- +Closure 033: var_dump() of a Closure +--FILE-- +<?php + +$outer = 25; + +class Test { + public $func1; + public $var = 42; + function __construct() { + global $outer; + $this->func1 = function($param, $other = "default") use ($outer) { + }; + } +} + +$o = new Test; +var_dump($o->func1); + +$o->func2 = function($param, $other = "default") use ($outer) { +}; + +var_dump($o->func2); + +$func3 = function($param, $other = "default") use ($outer) { +}; + +var_dump($func3); + +?> +===DONE=== +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + ["func1"]=> + object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + ["func1"]=> + object(Closure)#%d (3) { + ["this"]=> + *RECURSION* + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + ["var"]=> + int(42) + } + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + ["var"]=> + int(42) + } + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + ["outer"]=> + &int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +===DONE=== Index: Zend/tests/closure_034.phpt =================================================================== RCS file: Zend/tests/closure_034.phpt diff -N Zend/tests/closure_034.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_034.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,45 @@ +--TEST-- +Closure 034: Calling property Closure +--FILE-- +<?php + +class Test { + public $func; + function __construct() { + $this->func = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +ReflectionProperty::export($o, 'func'); +var_dump($o->func); +$f = $o->func; +$f(); +$o->func(); + +?> +===DONE=== +--EXPECTF-- +Property [ <default> public $func ] + +object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + ["func"]=> + object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + ["func"]=> + object(Closure)#%d (1) { + ["this"]=> + *RECURSION* + } + } + } + } +} +Test::{closure}() +Test::{closure}() +===DONE=== Index: Zend/tests/closure_035.phpt =================================================================== RCS file: Zend/tests/closure_035.phpt diff -N Zend/tests/closure_035.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_035.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,46 @@ +--TEST-- +Closure 035: Calling property supporting __invoke +--FILE-- +<?php + +class Curry +{ + protected $callable; + protected $args; + + public static function create($callable) + { + $curry = new self($callable, array_slice(func_get_args(), 1)); + return $curry; + } + + protected function __construct($callable, $args) + { + $this->callable = $callable; + $this->args = $args; + } + + public function __invoke() + { + return call_user_func_array($this->callable, array_merge($this->args, func_get_args())); + } +} + +$d = new DateTime(); +$getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($getAtom)); +var_dump($getAtom()); + +$d = new DateTime(); +$d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($d->getAtom)); +var_dump($d->getAtom()); + +?> +===DONE=== +--EXPECTF-- +bool(true) +string(%d) "2%d-%d-%dT%d:%d:%d%s" +bool(true) +string(%d) "2%d-%d-%dT%d:%d:%d%s" +===DONE=== Index: Zend/tests/closure_036.phpt =================================================================== RCS file: Zend/tests/closure_036.phpt diff -N Zend/tests/closure_036.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_036.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,43 @@ +--TEST-- +Closure 036: Calling dynamic property Closure (not supported) +--FILE-- +<?php + +class Test { + public $methods = array(); + function __isset($name) { + echo __METHOD__ . "($name)\n"; + return isset($this->methods[$name]); + } + function __get($name) { + echo __METHOD__ . "($name)\n"; + return $this->methods[$name]; + } + function __set($name, $data) { + echo __METHOD__ . "($name)\n"; + $this->methods[$name] = $data; + } + function __toString() { + return __CLASS__; + } +} + +$o = new Test; +var_dump(isset($o->func)); +$o->func = function() { + echo __METHOD__ . "(" . var_export($this, true) . ")\n"; +}; +var_dump(isset($o->func)); +$o->func(); +var_dump($o->func); + +?> +===DONE=== +--EXPECTF-- +Test::__isset(func) +bool(false) +Test::__set(func) +Test::__isset(func) +bool(true) + +Fatal error: Call to undefined method Test::func() in %sclosure_036.php on line %d Index: Zend/tests/closure_037.phpt =================================================================== RCS file: Zend/tests/closure_037.phpt diff -N Zend/tests/closure_037.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_037.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,31 @@ +--TEST-- +Closure 037: Calling closure property via variable +--FILE-- +<?php + +class Test { + public $func1; + function __construct() { + $this->func1 = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +$n = 'func1'; +$o->$n(); + +$o->func2 = function() { + echo __METHOD__ . "()\n"; +}; + +$n = 'func2'; +$o->$n(); + +?> +===DONE=== +--EXPECTF-- +Test::{closure}() +{closure}() +===DONE=== Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.208 diff -u -p -d -r1.208 zend_object_handlers.c --- Zend/zend_object_handlers.c 31 Dec 2008 11:12:29 -0000 1.208 +++ Zend/zend_object_handlers.c 1 Jan 2009 16:48:56 -0000 @@ -801,6 +801,24 @@ static union _zend_function *zend_std_ge zobj = Z_OBJ_P(object); if (zend_u_hash_find(&zobj->ce->function_table, type, lc_method_name, lc_method_name_len+1, (void **)&fbc) == FAILURE) { + zval **callable, member; + zend_property_info *property_info; + + ZVAL_TEXTL(&member, method_name, method_len, 0); + property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC); + + if (property_info && zend_u_hash_quick_find(zobj->properties, Z_TYPE(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &callable) == SUCCESS) { + zval *callable_obj; + zend_class_entry *ce_ptr; + + if (Z_TYPE_PP(callable) == IS_OBJECT + && Z_OBJ_HANDLER_PP(callable, get_closure) + && Z_OBJ_HANDLER_PP(callable, get_closure)(*callable, &ce_ptr, &fbc, &callable_obj TSRMLS_CC) == SUCCESS) { + *object_ptr = callable_obj; + efree(lc_method_name.v); + return fbc; + } + } efree(lc_method_name.v); if (zobj->ce->__call) { zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); Index: Zend/tests/closure_033.phpt =================================================================== RCS file: Zend/tests/closure_033.phpt diff -N Zend/tests/closure_033.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_033.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,122 @@ +--TEST-- +Closure 033: var_dump() of a Closure +--FILE-- +<?php + +$outer = 25; + +class Test { + public $func1; + public $var = 42; + function __construct() { + global $outer; + $this->func1 = function($param, $other = "default") use ($outer) { + }; + } +} + +$o = new Test; +var_dump($o->func1); + +$o->func2 = function($param, $other = "default") use ($outer) { +}; + +var_dump($o->func2); + +$func3 = function($param, $other = "default") use ($outer) { +}; + +var_dump($func3); + +?> +===DONE=== +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + [u"func1"]=> + object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + [u"func1"]=> + object(Closure)#%d (3) { + ["this"]=> + *RECURSION* + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + [u"var"]=> + int(42) + } + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + [u"var"]=> + int(42) + } + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + [u"outer"]=> + &int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +===DONE=== Index: Zend/tests/closure_034.phpt =================================================================== RCS file: Zend/tests/closure_034.phpt diff -N Zend/tests/closure_034.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_034.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,45 @@ +--TEST-- +Closure 034: Calling property Closure +--FILE-- +<?php + +class Test { + public $func; + function __construct() { + $this->func = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +ReflectionProperty::export($o, 'func'); +var_dump($o->func); +$f = $o->func; +$f(); +$o->func(); + +?> +===DONE=== +--EXPECTF-- +Property [ <default> public $func ] + +object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + [u"func"]=> + object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + [u"func"]=> + object(Closure)#%d (1) { + ["this"]=> + *RECURSION* + } + } + } + } +} +Test::{closure}() +Test::{closure}() +===DONE=== Index: Zend/tests/closure_035.phpt =================================================================== RCS file: Zend/tests/closure_035.phpt diff -N Zend/tests/closure_035.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_035.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,46 @@ +--TEST-- +Closure 035: Calling property supporting __invoke +--FILE-- +<?php + +class Curry +{ + protected $callable; + protected $args; + + public static function create($callable) + { + $curry = new self($callable, array_slice(func_get_args(), 1)); + return $curry; + } + + protected function __construct($callable, $args) + { + $this->callable = $callable; + $this->args = $args; + } + + public function __invoke() + { + return call_user_func_array($this->callable, array_merge($this->args, func_get_args())); + } +} + +$d = new DateTime(); +$getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($getAtom)); +var_dump($getAtom()); + +$d = new DateTime(); +$d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($d->getAtom)); +var_dump($d->getAtom()); + +?> +===DONE=== +--EXPECTF-- +bool(true) +unicode(%d) "2%d-%d-%dT%d:%d:%d%s" +bool(true) +unicode(%d) "2%d-%d-%dT%d:%d:%d%s" +===DONE=== Index: Zend/tests/closure_036.phpt =================================================================== RCS file: Zend/tests/closure_036.phpt diff -N Zend/tests/closure_036.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_036.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,43 @@ +--TEST-- +Closure 036: Calling dynamic property Closure (not supported) +--FILE-- +<?php + +class Test { + public $methods = array(); + function __isset($name) { + echo __METHOD__ . "($name)\n"; + return isset($this->methods[$name]); + } + function __get($name) { + echo __METHOD__ . "($name)\n"; + return $this->methods[$name]; + } + function __set($name, $data) { + echo __METHOD__ . "($name)\n"; + $this->methods[$name] = $data; + } + function __toString() { + return __CLASS__; + } +} + +$o = new Test; +var_dump(isset($o->func)); +$o->func = function() { + echo __METHOD__ . "(" . var_export($this, true) . ")\n"; +}; +var_dump(isset($o->func)); +$o->func(); +var_dump($o->func); + +?> +===DONE=== +--EXPECTF-- +Test::__isset(func) +bool(false) +Test::__set(func) +Test::__isset(func) +bool(true) + +Fatal error: Call to undefined method Test::func() in %sclosure_036.php on line %d Index: Zend/tests/closure_037.phpt =================================================================== RCS file: Zend/tests/closure_037.phpt diff -N Zend/tests/closure_037.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_037.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,31 @@ +--TEST-- +Closure 037: Calling closure property via variable +--FILE-- +<?php + +class Test { + public $func1; + function __construct() { + $this->func1 = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +$n = 'func1'; +$o->$n(); + +$o->func2 = function() { + echo __METHOD__ . "()\n"; +}; + +$n = 'func2'; +$o->$n(); + +?> +===DONE=== +--EXPECTF-- +Test::{closure}() +{closure}() +===DONE===
Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.135.2.6.2.22.2.23 diff -u -p -d -r1.135.2.6.2.22.2.23 zend_object_handlers.c --- Zend/zend_object_handlers.c 31 Dec 2008 11:15:32 -0000 1.135.2.6.2.22.2.23 +++ Zend/zend_object_handlers.c 1 Jan 2009 16:48:22 -0000 @@ -791,6 +791,24 @@ static union _zend_function *zend_std_ge zobj = Z_OBJ_P(object); if (zend_hash_find(&zobj->ce->function_table, lc_method_name, method_len+1, (void **)&fbc) == FAILURE) { + zval **callable, member; + zend_property_info *property_info; + + ZVAL_STRINGL(&member, method_name, method_len, 0); + property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC); + + if (property_info && zend_hash_quick_find(zobj->properties, property_info->name, property_info->name_length+1, property_info->h, (void **) &callable) == SUCCESS) { + zval *callable_obj; + zend_class_entry *ce_ptr; + + if (Z_TYPE_PP(callable) == IS_OBJECT + && Z_OBJ_HANDLER_PP(callable, get_closure) + && Z_OBJ_HANDLER_PP(callable, get_closure)(*callable, &ce_ptr, &fbc, &callable_obj TSRMLS_CC) == SUCCESS) { + *object_ptr = callable_obj; + free_alloca(lc_method_name, use_heap); + return fbc; + } + } free_alloca(lc_method_name, use_heap); if (zobj->ce->__call) { zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); Index: Zend/tests/closure_033.phpt =================================================================== RCS file: Zend/tests/closure_033.phpt diff -N Zend/tests/closure_033.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_033.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,122 @@ +--TEST-- +Closure 033: var_dump() of a Closure +--FILE-- +<?php + +$outer = 25; + +class Test { + public $func1; + public $var = 42; + function __construct() { + global $outer; + $this->func1 = function($param, $other = "default") use ($outer) { + }; + } +} + +$o = new Test; +var_dump($o->func1); + +$o->func2 = function($param, $other = "default") use ($outer) { +}; + +var_dump($o->func2); + +$func3 = function($param, $other = "default") use ($outer) { +}; + +var_dump($func3); + +?> +===DONE=== +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + ["func1"]=> + object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + ["func1"]=> + object(Closure)#%d (3) { + ["this"]=> + *RECURSION* + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + ["var"]=> + int(42) + } + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + ["var"]=> + int(42) + } + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + ["outer"]=> + &int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + ["outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +===DONE=== Index: Zend/tests/closure_034.phpt =================================================================== RCS file: Zend/tests/closure_034.phpt diff -N Zend/tests/closure_034.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_034.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,45 @@ +--TEST-- +Closure 034: Calling property Closure +--FILE-- +<?php + +class Test { + public $func; + function __construct() { + $this->func = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +ReflectionProperty::export($o, 'func'); +var_dump($o->func); +$f = $o->func; +$f(); +$o->func(); + +?> +===DONE=== +--EXPECTF-- +Property [ <default> public $func ] + +object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + ["func"]=> + object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + ["func"]=> + object(Closure)#%d (1) { + ["this"]=> + *RECURSION* + } + } + } + } +} +Test::{closure}() +Test::{closure}() +===DONE=== Index: Zend/tests/closure_035.phpt =================================================================== RCS file: Zend/tests/closure_035.phpt diff -N Zend/tests/closure_035.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_035.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,46 @@ +--TEST-- +Closure 035: Calling property supporting __invoke +--FILE-- +<?php + +class Curry +{ + protected $callable; + protected $args; + + public static function create($callable) + { + $curry = new self($callable, array_slice(func_get_args(), 1)); + return $curry; + } + + protected function __construct($callable, $args) + { + $this->callable = $callable; + $this->args = $args; + } + + public function __invoke() + { + return call_user_func_array($this->callable, array_merge($this->args, func_get_args())); + } +} + +$d = new DateTime(); +$getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($getAtom)); +var_dump($getAtom()); + +$d = new DateTime(); +$d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($d->getAtom)); +var_dump($d->getAtom()); + +?> +===DONE=== +--EXPECTF-- +bool(true) +string(%d) "2%d-%d-%dT%d:%d:%d%s" +bool(true) +string(%d) "2%d-%d-%dT%d:%d:%d%s" +===DONE=== Index: Zend/tests/closure_036.phpt =================================================================== RCS file: Zend/tests/closure_036.phpt diff -N Zend/tests/closure_036.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_036.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,43 @@ +--TEST-- +Closure 036: Calling dynamic property Closure (not supported) +--FILE-- +<?php + +class Test { + public $methods = array(); + function __isset($name) { + echo __METHOD__ . "($name)\n"; + return isset($this->methods[$name]); + } + function __get($name) { + echo __METHOD__ . "($name)\n"; + return $this->methods[$name]; + } + function __set($name, $data) { + echo __METHOD__ . "($name)\n"; + $this->methods[$name] = $data; + } + function __toString() { + return __CLASS__; + } +} + +$o = new Test; +var_dump(isset($o->func)); +$o->func = function() { + echo __METHOD__ . "(" . var_export($this, true) . ")\n"; +}; +var_dump(isset($o->func)); +$o->func(); +var_dump($o->func); + +?> +===DONE=== +--EXPECTF-- +Test::__isset(func) +bool(false) +Test::__set(func) +Test::__isset(func) +bool(true) + +Fatal error: Call to undefined method Test::func() in %sclosure_036.php on line %d Index: Zend/tests/closure_037.phpt =================================================================== RCS file: Zend/tests/closure_037.phpt diff -N Zend/tests/closure_037.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_037.phpt 1 Jan 2009 16:48:23 -0000 @@ -0,0 +1,31 @@ +--TEST-- +Closure 037: Calling closure property via variable +--FILE-- +<?php + +class Test { + public $func1; + function __construct() { + $this->func1 = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +$n = 'func1'; +$o->$n(); + +$o->func2 = function() { + echo __METHOD__ . "()\n"; +}; + +$n = 'func2'; +$o->$n(); + +?> +===DONE=== +--EXPECTF-- +Test::{closure}() +{closure}() +===DONE=== Index: Zend/zend_object_handlers.c =================================================================== RCS file: /repository/ZendEngine2/zend_object_handlers.c,v retrieving revision 1.208 diff -u -p -d -r1.208 zend_object_handlers.c --- Zend/zend_object_handlers.c 31 Dec 2008 11:12:29 -0000 1.208 +++ Zend/zend_object_handlers.c 1 Jan 2009 16:48:56 -0000 @@ -801,6 +801,24 @@ static union _zend_function *zend_std_ge zobj = Z_OBJ_P(object); if (zend_u_hash_find(&zobj->ce->function_table, type, lc_method_name, lc_method_name_len+1, (void **)&fbc) == FAILURE) { + zval **callable, member; + zend_property_info *property_info; + + ZVAL_TEXTL(&member, method_name, method_len, 0); + property_info = zend_get_property_info(zobj->ce, &member, 1 TSRMLS_CC); + + if (property_info && zend_u_hash_quick_find(zobj->properties, Z_TYPE(member), property_info->name, property_info->name_length+1, property_info->h, (void **) &callable) == SUCCESS) { + zval *callable_obj; + zend_class_entry *ce_ptr; + + if (Z_TYPE_PP(callable) == IS_OBJECT + && Z_OBJ_HANDLER_PP(callable, get_closure) + && Z_OBJ_HANDLER_PP(callable, get_closure)(*callable, &ce_ptr, &fbc, &callable_obj TSRMLS_CC) == SUCCESS) { + *object_ptr = callable_obj; + efree(lc_method_name.v); + return fbc; + } + } efree(lc_method_name.v); if (zobj->ce->__call) { zend_internal_function *call_user_call = emalloc(sizeof(zend_internal_function)); Index: Zend/tests/closure_033.phpt =================================================================== RCS file: Zend/tests/closure_033.phpt diff -N Zend/tests/closure_033.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_033.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,122 @@ +--TEST-- +Closure 033: var_dump() of a Closure +--FILE-- +<?php + +$outer = 25; + +class Test { + public $func1; + public $var = 42; + function __construct() { + global $outer; + $this->func1 = function($param, $other = "default") use ($outer) { + }; + } +} + +$o = new Test; +var_dump($o->func1); + +$o->func2 = function($param, $other = "default") use ($outer) { +}; + +var_dump($o->func2); + +$func3 = function($param, $other = "default") use ($outer) { +}; + +var_dump($func3); + +?> +===DONE=== +--EXPECTF-- +object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + [u"func1"]=> + object(Closure)#%d (3) { + ["this"]=> + object(Test)#%d (2) { + [u"func1"]=> + object(Closure)#%d (3) { + ["this"]=> + *RECURSION* + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + [u"var"]=> + int(42) + } + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } + } + [u"var"]=> + int(42) + } + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + [u"outer"]=> + &int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +object(Closure)#%d (3) { + ["this"]=> + NULL + ["static"]=> + array(1) { + [u"outer"]=> + int(25) + } + ["parameter"]=> + array(2) { + ["$param"]=> + string(10) "<required>" + ["$other"]=> + string(10) "<optional>" + } +} +===DONE=== Index: Zend/tests/closure_034.phpt =================================================================== RCS file: Zend/tests/closure_034.phpt diff -N Zend/tests/closure_034.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_034.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,45 @@ +--TEST-- +Closure 034: Calling property Closure +--FILE-- +<?php + +class Test { + public $func; + function __construct() { + $this->func = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +ReflectionProperty::export($o, 'func'); +var_dump($o->func); +$f = $o->func; +$f(); +$o->func(); + +?> +===DONE=== +--EXPECTF-- +Property [ <default> public $func ] + +object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + [u"func"]=> + object(Closure)#%d (1) { + ["this"]=> + object(Test)#%d (1) { + [u"func"]=> + object(Closure)#%d (1) { + ["this"]=> + *RECURSION* + } + } + } + } +} +Test::{closure}() +Test::{closure}() +===DONE=== Index: Zend/tests/closure_035.phpt =================================================================== RCS file: Zend/tests/closure_035.phpt diff -N Zend/tests/closure_035.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_035.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,46 @@ +--TEST-- +Closure 035: Calling property supporting __invoke +--FILE-- +<?php + +class Curry +{ + protected $callable; + protected $args; + + public static function create($callable) + { + $curry = new self($callable, array_slice(func_get_args(), 1)); + return $curry; + } + + protected function __construct($callable, $args) + { + $this->callable = $callable; + $this->args = $args; + } + + public function __invoke() + { + return call_user_func_array($this->callable, array_merge($this->args, func_get_args())); + } +} + +$d = new DateTime(); +$getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($getAtom)); +var_dump($getAtom()); + +$d = new DateTime(); +$d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM); +var_dump(is_Callable($d->getAtom)); +var_dump($d->getAtom()); + +?> +===DONE=== +--EXPECTF-- +bool(true) +unicode(%d) "2%d-%d-%dT%d:%d:%d%s" +bool(true) +unicode(%d) "2%d-%d-%dT%d:%d:%d%s" +===DONE=== Index: Zend/tests/closure_036.phpt =================================================================== RCS file: Zend/tests/closure_036.phpt diff -N Zend/tests/closure_036.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_036.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,43 @@ +--TEST-- +Closure 036: Calling dynamic property Closure (not supported) +--FILE-- +<?php + +class Test { + public $methods = array(); + function __isset($name) { + echo __METHOD__ . "($name)\n"; + return isset($this->methods[$name]); + } + function __get($name) { + echo __METHOD__ . "($name)\n"; + return $this->methods[$name]; + } + function __set($name, $data) { + echo __METHOD__ . "($name)\n"; + $this->methods[$name] = $data; + } + function __toString() { + return __CLASS__; + } +} + +$o = new Test; +var_dump(isset($o->func)); +$o->func = function() { + echo __METHOD__ . "(" . var_export($this, true) . ")\n"; +}; +var_dump(isset($o->func)); +$o->func(); +var_dump($o->func); + +?> +===DONE=== +--EXPECTF-- +Test::__isset(func) +bool(false) +Test::__set(func) +Test::__isset(func) +bool(true) + +Fatal error: Call to undefined method Test::func() in %sclosure_036.php on line %d Index: Zend/tests/closure_037.phpt =================================================================== RCS file: Zend/tests/closure_037.phpt diff -N Zend/tests/closure_037.phpt --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ Zend/tests/closure_037.phpt 1 Jan 2009 16:48:56 -0000 @@ -0,0 +1,31 @@ +--TEST-- +Closure 037: Calling closure property via variable +--FILE-- +<?php + +class Test { + public $func1; + function __construct() { + $this->func1 = function() { + echo __METHOD__ . "()\n"; + }; + } +} + +$o = new Test; +$n = 'func1'; +$o->$n(); + +$o->func2 = function() { + echo __METHOD__ . "()\n"; +}; + +$n = 'func2'; +$o->$n(); + +?> +===DONE=== +--EXPECTF-- +Test::{closure}() +{closure}() +===DONE===