# __invoke() weirdness

- Thread: https://externals.io/message/42368
- Original archive: https://news-web.php.net/php.internals/42368
- Started by david.zuelke@bitextender.com on 2008-12-23T16:02:43+00:00
- Messages: 25
- Reply by email to internals@lists.php.net
- More for agents: search with https://externals.io/search.md?q={query} · JSON API at https://externals.io/api · documentation at https://externals.io/llms.txt

---

## #42368 — david.zuelke@bitextender.com — 2008-12-23T16:02:43+00:00

URL: https://externals.io/message/42368#42368

Hi folks,

I played with __invoke today:

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()`));
   }
}

However, it doesn't work consistently.

This works fine:
   $d = new DateTime();
   $getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
   echo $getAtom();

This gives a fatal "Call to undefined method DateTime::getAtom()"
   $d = new DateTime();
   $d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
   echo $d->getAtom();

Is that intentional?

Cheers,

David

---

## #42369 — Stanislav Malyshev — 2008-12-23T16:55:42+00:00

URL: https://externals.io/message/42368#42369 · In reply to: #42368

Hi!

> This gives a fatal "Call to undefined method DateTime::getAtom()"
>   $d = new DateTime();
>   $d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
>   echo $d->getAtom();

This syntax means "call method getAtom of object $d". PHP has no way of 
reusing same syntax for two different things, at least for now.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #42372 — Lars Strojny — 2008-12-25T05:11:23+00:00

URL: https://externals.io/message/42368#42372 · In reply to: #42368

Hi David,

Am Dienstag, den 23.12.2008, 17:02 +0100 schrieb David Zülke:
[...]
> This gives a fatal "Call to undefined method DateTime::getAtom()"
>    $d = new DateTime();
>    $d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
>    echo $d->getAtom();

This is the same as the following:

$obj = new stdClass();
$obj->method = function() {
   return "foo";
};
$obj->method();

The first "method" is a property, the call to "method" is - well - a
method call. Because PHP separates methods and properties in the class
entry structure - and because we have magic methods like __set, __get
and __call, there is no efficient and logical way how to search for
properties with closure instances when a method is not found. We
discussed that before and decided to let closures go out into the wild
and think about adding a solution for prototype alike inheritance later.

cu, Lars
-- 
   Jabber: lars@strojny.net
   Weblog: http://usrportage.de

---

## #42392 — Ionut Gabriel Stan — 2008-12-29T00:18:14+00:00

URL: https://externals.io/message/42368#42392 · In reply to: #42372

PHP written in a JavaScript style is already possible with PHP 5.3. I 
started a little
thread about this on a forum but didn't get that much feedback:

http://www.sitepoint.com/forums/showthread.php?t=565576

It's not really what we're used to in JavaScript but it's very close.




> Hi David,
>
> Am Dienstag, den 23.12.2008, 17:02 +0100 schrieb David Zülke:
> [...]
>    
> > […]
>
> This is the same as the following:
>
> $obj = new stdClass();
> $obj->method = function() {
>     return "foo";
> };
> $obj->method();
>
> The first "method" is a property, the call to "method" is - well - a
> method call. Because PHP separates methods and properties in the class
> entry structure - and because we have magic methods like __set, __get
> and __call, there is no efficient and logical way how to search for
> properties with closure instances when a method is not found. We
> discussed that before and decided to let closures go out into the wild
> and think about adding a solution for prototype alike inheritance later.
>
> cu, Lars

---

## #42393 — david.zuelke@bitextender.com — 2008-12-29T05:00:38+00:00

URL: https://externals.io/message/42368#42393 · In reply to: #42372

> Hi David,
>
> Am Dienstag, den 23.12.2008, 17:02 +0100 schrieb David Zülke:
> [...]
> > […]
>
> This is the same as the following:
>
> $obj = new stdClass();
> $obj->method = function() {
>   return "foo";
> };
> $obj->method();
>
> The first "method" is a property, the call to "method" is - well - a
> method call. Because PHP separates methods and properties in the class
> entry structure - and because we have magic methods like __set, __get
> and __call, there is no efficient and logical way how to search for
> properties with closure instances when a method is not found. We
> discussed that before and decided to let closures go out into the wild
> and think about adding a solution for prototype alike inheritance  
> later.

I realize that, but I guess it's pretty inconsistent, as I can do
- $func();
- $func->__invoke();
- $obj->func->__invoke();
but not
- $obj->func();

That was my point; I'm aware of potential reasons for this behavior  
(like what Stas pointed out). Sorry for the confusion ;)

Is it going to be like this forever? Stas said "there is no way to  
distinguish". Isn't that a parser issue that should be gone now with  
the switch to re2c? Wouldn't it be possible to just do a lookup for a  
property that is an object with __invoke if the method was not found?

- David

---

## #42394 — Etienne Kneuss — 2008-12-29T07:01:46+00:00

URL: https://externals.io/message/42368#42394 · In reply to: #42393

Hello,

On Mon, Dec 29, 2008 at 6:00 AM, David Zülke
<david.zuelke@bitextender.com> wrote:
> 
>
> > […]
>
> I realize that, but I guess it's pretty inconsistent, as I can do
> - $func();
> - $func->__invoke();
> - $obj->func->__invoke();
> but not
> - $obj->func();
>
> That was my point; I'm aware of potential reasons for this behavior (like
> what Stas pointed out). Sorry for the confusion ;)
>
> Is it going to be like this forever? Stas said "there is no way to
> distinguish". Isn't that a parser issue that should be gone now with the
> switch to re2c? Wouldn't it be possible to just do a lookup for a property
> that is an object with __invoke if the method was not found?

Well, this is not a parser problem (so re2c, which actually is the
lexer, won't help).
Additionally: looking for a property when a method is not found would
break code relying on __call() to be called on those cases.

Finally, note that you can do that in userland using something looking like:
public function __call($name, $args) { return
call_user_func_array(array($this->$name, "__invoke"), $args); }

Regards,

>
> - David
> --
> 
> 
>
>
>
>



-- 
Etienne Kneuss
http://www.colder.ch

Men never do evil so completely and cheerfully as
when they do it from a religious conviction.
-- Pascal

---

## #42395 — Jack Allnutt — 2008-12-29T07:09:32+00:00

URL: https://externals.io/message/42368#42395 · In reply to: #42394

Etienne Kneuss wrote:
> Finally, note that you can do that in userland using something looking like:
> public function __call($name, $args) { return
> call_user_func_array(array($this->$name, "__invoke"), $args); }
It seems like it'd make more sense to allow *all* functions to be called 
using the 'normal' syntax without having to resort to that userland 
workaround.

---

## #42396 — Lars Strojny — 2008-12-29T08:28:46+00:00

URL: https://externals.io/message/42368#42396 · In reply to: #42395

Hi Jack,

Am Montag, den 29.12.2008, 07:09 +0000 schrieb Jack Allnutt:
[...]
> It seems like it'd make more sense to allow *all* functions to be
> called using the 'normal' syntax without having to resort to that
> userland workaround.

Sometimes I really wish people would read the mails sent to this list.
Again: yes, it would be nice and yes, we thought about it too. But we
didn't find a way to do this in an efficient manner (there might be one
using an interface to indicate a different behaviour of object
resolution, but interfaces might not be the right way to do it). But if
you have a patch which implements that feature efficiently, please
demonstrate that we are idiots and submit that patch. I would be really
happy about that. Otherwise arguing what should be doesn't help that
much, sorry.

Thanks!
Lars
-- 
   Jabber: lars@strojny.net
   Weblog: http://usrportage.de

---

## #42421 — Marcus Boerger — 2008-12-31T16:38:18+00:00

URL: https://externals.io/message/42368#42421 · In reply to: #42368

Hello David,

Tuesday, December 23, 2008, 5:02:43 PM, you wrote:

> Hi folks,

> I played with __invoke today:

> 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- 
> > […]
>    }
> }

> However, it doesn't work consistently.

> This works fine:
>    $d = new DateTime();
>    $getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
>    echo $getAtom();

> This gives a fatal "Call to undefined method DateTime::getAtom()"
>    $d = new DateTime();
>    $d->getAtom = Curry::create(array($d, 'format'), DATE_ATOM);
>    echo $d->getAtom();

> Is that intentional?

So far it is. Yet I as much as you do not like the inconsistency. So I
spend a little bit on providing the following patch that should do what
you were looking for.

The disadvantage: Calling properties is case sensitive while calling
methods isn't. But since this has nothign to do with this patch and the
patch only increases consistency I am all for applying it.

Comments? Lukas/Johannes?

Oh I hate that case insensitivity.... and inconsistency....

> Cheers,

> David





Best regards,
 Marcus

---

## #42422 — Sebastian Bergmann — 2008-12-31T17:30:23+00:00

URL: https://externals.io/message/42368#42422 · In reply to: #42421

Marcus Boerger schrieb:
> Oh I hate that case insensitivity.... and inconsistency....

 I am all for reducing inconsistencies (and not introducing new ones :-).

-- 
Sebastian Bergmann                          http://sebastian-bergmann.de/
GnuPG Key: 0xB85B5D69 / 27A7 2B14 09E4 98CD 6277 0E5B 6867 C514 B85B 5D69

---

## #42424 — Lars Strojny — 2008-12-31T17:59:08+00:00

URL: https://externals.io/message/42368#42424 · In reply to: #42421

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?

cu, Lars

Am Mittwoch, den 31.12.2008, 17:38 +0100 schrieb Marcus Boerger:
> Hello David,
> 
> Tuesday, December 23, 2008, 5:02:43 PM, you wrote:
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> > […]
> 
> So far it is. Yet I as much as you do not like the inconsistency. So I
> spend a little bit on providing the following patch that should do what
> you were looking for.
> 
> The disadvantage: Calling properties is case sensitive while calling
> methods isn't. But since this has nothign to do with this patch and the
> patch only increases consistency I am all for applying it.
> 
> Comments? Lukas/Johannes?
> 
> Oh I hate that case insensitivity.... and inconsistency....
> 
> > […]
> 
> > […]
> 
> 
> 
> 
> 
> Best regards,
>  Marcus
> --  

-- 
   Jabber: lars@strojny.net
   Weblog: http://usrportage.de

---

## #42426 — Marcus Boerger — 2008-12-31T19:12:59+00:00

URL: https://externals.io/message/42368#42426 · In reply to: #42424

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
    if (is_callable($callable)) {
      return call_user_func_array($callable, $args);
    }
  }
  // Original behavior:
  // Check for __call
  if (method_exists($this, '__call')) {
    return $this->__call($name, $args);
  }
  // error
  error_log('Function ' . __CLASS__ . '::' . $name . ' not found');
  return NULL;
}

As to the performance impact. We add one additional hash-lookup per
method call on a default class for a non found function. So whenever
we would normally call __call we add an additional lookup.

> cu, Lars

> Am Mittwoch, den 31.12.2008, 17:38 +0100 schrieb Marcus Boerger:
> > […]




Best regards,
 Marcus

---

## #42427 — Hannes Magnusson — 2008-12-31T19:33:43+00:00

URL: https://externals.io/message/42368#42427 · In reply to: #42426

> Hello Lars,
>
> Wednesday, December 31, 2008, 6:59:08 PM, you wrote:
>
> > […]
>
> > […]
>
> 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:

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

---

## #42435 — Marcus Boerger — 2009-01-01T15:09:39+00:00

URL: https://externals.io/message/42368#42435 · In reply to: #42427

Hello Hannes,

Wednesday, December 31, 2008, 8:33:43 PM, you wrote:

> 
> > […]

> 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

---

## #42437 — Marcus Boerger — 2009-01-01T16:06:23+00:00

URL: https://externals.io/message/42368#42437 · In reply to: #42435

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:

> > […]

> > […]

> 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.

> > […]

> > […]

> > […]

> > […]




> Best regards,
>  Marcus



Best regards,
 Marcus

---

## #42438 — david.zuelke@bitextender.com — 2009-01-01T16:23:08+00:00

URL: https://externals.io/message/42368#42438 · In reply to: #42437

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




> 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:
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
>
>
>
> > […]
>
>
>
> Best regards,
> Marcus<ze2-callable-properties-5.3-20090101-b.diff.txt

---

## #42439 — Marcus Boerger — 2009-01-01T16:50:39+00:00

URL: https://externals.io/message/42368#42439 · In reply to: #42438

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- 
> > […]
> 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


> 

> > […]





Best regards,
 Marcus

---

## #42442 — david.zuelke@bitextender.com — 2009-01-02T07:03:22+00:00

URL: https://externals.io/message/42368#42442 · In reply to: #42439

Marcus,

thanks!

Why is it
Test::{closure}()
{closure}()

and not
Test::{closure}()
Test::{closure}()

in that test, though? Is it because func1() "was there" from the  
Engine's POV after the ctor was called? AFAICT, that's the only  
difference between the two.

Cheers,

- David





> Hello David,
>
>  I added test closure_037.phpt to demonstrate this.
>
> marcus
>
> Thursday, January 1, 2009, 5:23:08 PM, you wrote:
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
> > […]
>
>
> > […]
>
> > […]
>
>
>
>
>
> Best regards,
> Marcus<ze2-callable-properties-5.3-20090101-d.diff.txt><ze2-callable- 
> properties-6.0-20090101-d.diff.txt

---

## #42443 — Marcus Boerger — 2009-01-02T12:05:46+00:00

URL: https://externals.io/message/42368#42443 · In reply to: #42442

Hello David,

Friday, January 2, 2009, 8:03:22 AM, you wrote:

> Marcus,

> thanks!

> Why is it
> Test::{closure}()
> {closure}()

> and not
> Test::{closure}()
> Test::{closure}()

> in that test, though? Is it because func1() "was there" from the  
> Engine's POV after the ctor was called? AFAICT, that's the only  
> difference between the two.

No, one is created inside the object instance and one is created outside.
So one has a bound $this pointer while the other has not. And the class
comes from the bound $this. So if there is no bound $this there is no class
and thus the closure becomes a function rather than a method.

marcus

> Cheers,

> - David



> 

> > […]




Best regards,
 Marcus

---

## #42451 — johannes@php.net — 2009-01-02T18:16:32+00:00

URL: https://externals.io/message/42368#42451 · In reply to: #42421

Hi,


> So far it is. Yet I as much as you do not like the inconsistency. So I
> spend a little bit on providing the following patch that should do
> what
> you were looking for.

I thought bout this issue for one day or so, there are three things to
consider here:

- the language design question
- implementation-related things
- getting 5.3 out

For the first question I have to say that this is a major change to
language. Bringing this feature in makes it a mixture of a class-based
OO model and JavaScript-like class-less OO model. As I tend to read way
more code than I write I, personally, prefer the clearer class based
approach.

Nonetheless there seems to be large support for this, so let's lake a
deeper look:

> The disadvantage: Calling properties is case sensitive while calling
> methods isn't. But since this has nothign to do with this patch and
> the patch only increases consistency I am all for applying it.

That's one of the symptoms of the underlying problem we're having: In
our object implementation methods and properties are kept completely
separated. Using properties now like methods will have side effects, one
is described above. I found another one today:

<?php
class A {
    public $p;
    public function __construct() {
        $this->p = function() { echo 1; };
    }
}

class B extends A {
    private function p() {
        echo 2;
    }
}

$a = new A();
$b = new B();

$a->p();
$b->p();
?>

What do you expect? Well, the first call, to $a->p(); works, the second
one, to $b->p();, doesn't since where accessing the private method
B::p().

$ sapi/cli/php test.php
1
Fatal error: Call to undefined method B::p() in test.php on line 16

Depending on your view on this that result can be correct or wrong, it
might even be wrong in different ways, maybe overwriting $a->p with
$b->p should be forbidden, maybe it should treat private elements like
non-existent ones.

The obvious thing is that it feels like the "is-a" relationship isn't
enforced anymore.

Additionally there are other areas with such conflicts: Reflection
(ReflectionMethod doesn't work, ReflectionProperty has no hint it's
executable, ...) and other meta-functionality are obvious, others might
(and will) be quite hidden.

I guess the only solid way to implement that feature would be by merging
the property and method tables into an "element" table so we reduce such
conflicts -- while that's a major engine and language change. Every
other approach will have side-effects.

This leads us to the third consideration: 5.3 is around 1.5 years in
development now with lots of new features. It was announced that we
wanted to go to a beta status to get it out soon ("release early,
release often" ...) 

I'd say it would be good to concentrate on making 5.3 stable and then
see how new features are accepted. If users really demand such a
functionality, when using closures for "real life" development, we can
still add it, but that should be done with the time needed to identify
side-effects and other consequences, not in a rush during holiday season
after a feature freeze has been announced.

johannes

---

## #42464 — Marcus Boerger — 2009-01-03T14:47:44+00:00

URL: https://externals.io/message/42368#42464 · In reply to: #42451

Hello Johannes,

Friday, January 2, 2009, 7:16:32 PM, you wrote:

> Hi,

> 
> > […]

> I thought bout this issue for one day or so, there are three things to
> consider here:

> - the language design question
> - implementation-related things
> - getting 5.3 out

> For the first question I have to say that this is a major change to
> language. Bringing this feature in makes it a mixture of a class-based
> OO model and JavaScript-like class-less OO model. As I tend to read way
> more code than I write I, personally, prefer the clearer class based
> approach.

> Nonetheless there seems to be large support for this, so let's lake a
> deeper look:

> > […]

> That's one of the symptoms of the underlying problem we're having: In
> our object implementation methods and properties are kept completely
> separated. Using properties now like methods will have side effects, one
> is described above. I found another one today:

> <?php
> class A {
>     public $p;
>     public function __construct() {
>         $this->p = function() { echo 1; };
>     }
> }

> class B extends A {
>     private function p() {
>         echo 2;
>     }
> }

> $a = new A();
> $b = new B();

> $a->p();
> $b->p();
?>>

> What do you expect? Well, the first call, to $a->p(); works, the second
> one, to $b->p();, doesn't since where accessing the private method
> B::p().

> $ sapi/cli/php test.php
> 1
> Fatal error: Call to undefined method B::p() in test.php on line 16

> Depending on your view on this that result can be correct or wrong, it
> might even be wrong in different ways, maybe overwriting $a->p with
> $b->p should be forbidden, maybe it should treat private elements like
> non-existent ones.

> The obvious thing is that it feels like the "is-a" relationship isn't
> enforced anymore.

First, for properties we already have that anyway. Remember you can add
properties to object instances at any time. So being able to add methods
at any time just reduces one more inconsistency between things. Also
since properties follow a mixture of class based and prototype based
object model we have to respect prototype semantics for the parts where
proptotype model is used. This means when you dynamically add a closure
then this is done in the manner of prototype semantics and needs to follow
it. Thus with a slightly different code the result is perfectly correct:

<?php
class A {
    // public $p;
}

class B extends A {
    private function p() {
        echo 2;
    }
}

$a = new A();
$b = new B();

$a->p = function() { echo 1; };
$a->p();
$b->p();
?>

So this raises the question what the status quo without the patch is:
class Test {
        public $func;
        function __construct() {
                $this->func = function() {
                        echo __METHOD__ . "()\n";
                };
        }
        private function func() {
                echo __METHOD__ . "()\n";
        }
}

$o = new Test;
$f = $o->func;
$f();                // Test::{closure}()
$o->func();          // Error call to private method...

So apparently we already have the exact issue at hand and we take
precedence in the actual function which in my opinion is a good
choice. And this also is exactly the behavior your example with the
patch applies shows.

To summarize this from my point of view we only get a bit more of
consistency. But for people with a JavaScript background we get a
lot more. As we now support what they are used to and that actually
is a pretty big advantage.

However if that is the intention, then something we might want to look
into in order to make it easier for those people is to fix something
that propbably looks wrong to them. That is binding a closure to a
property outside of the scope of the object does not bind the object
to the this_ptr as it would in case the assignment is done inside the
object scope (a class method). But once again this is somethign to be
fixed whether or not we apply the patch.

> Additionally there are other areas with such conflicts: Reflection
> (ReflectionMethod doesn't work, ReflectionProperty has no hint it's
> executable, ...) and other meta-functionality are obvious, others might
> (and will) be quite hidden.

Reflection now has closure support.

> I guess the only solid way to implement that feature would be by merging
> the property and method tables into an "element" table so we reduce such
> conflicts -- while that's a major engine and language change. Every
> other approach will have side-effects.

That would mean a major change in language and we should not go that route.

> This leads us to the third consideration: 5.3 is around 1.5 years in
> development now with lots of new features. It was announced that we
> wanted to go to a beta status to get it out soon ("release early,
> release often" ...) 

> I'd say it would be good to concentrate on making 5.3 stable and then
> see how new features are accepted. If users really demand such a
> functionality, when using closures for "real life" development, we can
> still add it, but that should be done with the time needed to identify
> side-effects and other consequences, not in a rush during holiday season
> after a feature freeze has been announced.

Stable also means stable features and semantics. And that includes trying
to get rid of inconsistencies.

marcus

Best regards,
 Marcus

---

## #42465 — Marcus Boerger — 2009-01-03T17:56:03+00:00

URL: https://externals.io/message/42368#42465 · In reply to: #42464

Hello all,

  current state of the patch is attached.

marcus

Saturday, January 3, 2009, 3:47:44 PM, you wrote:

> Hello Johannes,

> Friday, January 2, 2009, 7:16:32 PM, you wrote:

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]

> > […]
?>>>

> > […]

> > […]

> > […]

> > […]

> First, for properties we already have that anyway. Remember you can add
> properties to object instances at any time. So being able to add methods
> at any time just reduces one more inconsistency between things. Also
> since properties follow a mixture of class based and prototype based
> object model we have to respect prototype semantics for the parts where
> proptotype model is used. This means when you dynamically add a closure
> then this is done in the manner of prototype semantics and needs to follow
> it. Thus with a slightly different code the result is perfectly correct:

> <?php
> class A {
>     // public $p;
> }

> class B extends A {
>     private function p() {
>         echo 2;
>     }
> }

> $a = new A();
> $b = new B();

> $a->p = function() { echo 1; };
> $a->p();
> $b->p();
?>>

> So this raises the question what the status quo without the patch is:
> class Test {
>         public $func;
>         function __construct() {
>                 $this->func = function() {
>                         echo __METHOD__ . "()\n";
>                 };
>         }
>         private function func() {
>                 echo __METHOD__ . "()\n";
>         }
> }

> $o = new Test;
> $f = $o->func;
> $f();                // Test::{closure}()
> $o->func();          // Error call to private method...

> So apparently we already have the exact issue at hand and we take
> precedence in the actual function which in my opinion is a good
> choice. And this also is exactly the behavior your example with the
> patch applies shows.

> To summarize this from my point of view we only get a bit more of
> consistency. But for people with a JavaScript background we get a
> lot more. As we now support what they are used to and that actually
> is a pretty big advantage.

> However if that is the intention, then something we might want to look
> into in order to make it easier for those people is to fix something
> that propbably looks wrong to them. That is binding a closure to a
> property outside of the scope of the object does not bind the object
> to the this_ptr as it would in case the assignment is done inside the
> object scope (a class method). But once again this is somethign to be
> fixed whether or not we apply the patch.

> > […]

> Reflection now has closure support.

> > […]

> That would mean a major change in language and we should not go that route.

> > […]

> > […]

> Stable also means stable features and semantics. And that includes trying
> to get rid of inconsistencies.

> marcus

> Best regards,
>  Marcus




Best regards,
 Marcus

---

## #42470 — johannes@php.net — 2009-01-04T13:59:31+00:00

URL: https://externals.io/message/42368#42470 · In reply to: #42464

Hi,


> > […]
> 
> Stable also means stable features and semantics. And that includes trying
> to get rid of inconsistencies.

... fixing one inconsistency by introducing new inconsistencies? 

Like that one:

<?php
function foo() {}

$foo = "foo"; $foo(); // works
$bar = function(){};  // works

$o->foo = "foo";        $o->foo(); // will not work
$o->bar = function(){}; $o->bar(); // will work
?>

And I fear there will be more. Fixing all of them will take time so we
can delay 5.3 for another half year or so (... oh and during that time
we find another thing to change which will need proper invastigation,
too, so we delay again and then we find ....)

-> We should really try to shorten the release cycle, not making it
longer. Which, for 5.3, means we should concentrate on fixing bugs, not
adding features and change the language.

johannes

---

## #42471 — Kenan R Sulayman — 2009-01-04T15:20:06+00:00

URL: https://externals.io/message/42368#42471 · In reply to: #42470

Hi Johannes,

<?
    $o->foo = "foo";        $o->foo(); //Example 1
    $o->bar = function(){}; $o->bar();  //Example 2
?>

The example 1 must not work, because PHP tries to call $o->foo() as a
function, but not $o->foo as description for the function.

To make example 2 working, the declaration has to be changed.

Thanks,
--
(c) Kenan Sulayman
Freelance Designer and Programmer

Life's Live Poetry
http://MyJurnal.tk/

---

## #42522 — david.zuelke@bitextender.com — 2009-01-07T06:44:14+00:00

URL: https://externals.io/message/42368#42522 · In reply to: #42464

> However if that is the intention, then something we might want to look
> into in order to make it easier for those people is to fix something
> that propbably looks wrong to them. That is binding a closure to a
> property outside of the scope of the object does not bind the object
> to the this_ptr as it would in case the assignment is done inside the
> object scope (a class method). But once again this is somethign to be
> fixed whether or not we apply the patch.

Yes, I was actually going to write another reply to one of your last  
mails about exactly that.

Could that be done? I suppose it would be a good idea. However, what  
happens if you do the assignment from inside another class? Or is that  
$this currently not bound in such a case (I suppose it isn't, because  
that would be pretty confusing, but also pretty interesting in terms  
of what you can do with it).

- David
