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 <he...@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===
-- 
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to