# closures questions

- Thread: https://externals.io/message/39172
- Original archive: https://news-web.php.net/php.internals/39172
- Started by Lukas Kahwe Smith on 2008-07-22T11:04:03+00:00
- Messages: 16
- 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

---

## #39172 — Lukas Kahwe Smith — 2008-07-22T11:04:03+00:00

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

Hi,

I am sending this on behalf of Kalle:

1) Closures on class properties just don't work, the only way to do it  
is
to do something like:

$c = $a->b;
$c();

Calling: $a->b(); will result in method A::B() does not exists.

2) Closures can be defined as constants values because of its toString  
method:

define('Closure', function(){ echo 'Test'; });

echo constant('Closure'); /* (string) Closure object */

3) Since Closures have this toString method, but its not showing up in  
Reflection:

Reflection::export(new ReflectionClass('Closure'));

4) `var_export()` and Closures is useless, any call to `var_export()` like  
the example
below will all be the same:

$lambda = function()
{
     echo '&#923; Lambda';
};

var_export($lambda);

/*
Closure::__set_state(array(
))
*/

Maybe it could return some relevant information for exporting the  
closure across
data

5) Its impossible to clone a closure using the cloning keyword:

$a = function(){};
$b = clone $a;

This makes it hard to make a copy of the closure because of objects  
always are
passed by reference.

regards,
Lukas Kahwe Smith
mls@pooteeweet.org

---

## #39173 — Lukas Kahwe Smith — 2008-07-22T11:07:07+00:00

URL: https://externals.io/message/39172#39173 · In reply to: #39172

> 1) Closures on class properties just don't work, the only way to do  
> it is
> to do something like:
>
> $c = $a->b;
> $c();
>
> Calling: $a->b(); will result in method A::B() does not exists.

would be nice to get this fixed, but at worst it should be documented.

> 2) Closures can be defined as constants values because of its  
> toString method:
>
> define('Closure', function(){ echo 'Test'; });
>
> echo constant('Closure'); /* (string) Closure object */
>
> 3) Since Closures have this toString method, but its not showing up  
> in Reflection:
>
> Reflection::export(new ReflectionClass('Closure'));

so do we even want the toString() method?

> 4) `var_export()` and Closures is useless, any call to `var_export()`  
> like the example
> below will all be the same:
>
> $lambda = function()
> {
>    echo '&#923; Lambda';
> };
>
> var_export($lambda);
>
> /*
> Closure::__set_state(array(
> ))
> */
>
> Maybe it could return some relevant information for exporting the  
> closure across
> data

not a huge biggy to me.

> 5) Its impossible to clone a closure using the cloning keyword:
>
> $a = function(){};
> $b = clone $a;
>
> This makes it hard to make a copy of the closure because of objects  
> always are
> passed by reference.


I guess this is a draw back from the OO approach (rather than the  
original resource approach), but solvable?

regards,
Lukas Kahwe Smith
mls@pooteeweet.org

---

## #39180 — Christian Seiler — 2008-07-22T12:15:39+00:00

URL: https://externals.io/message/39172#39180 · In reply to: #39173

Hi Lukas,

> > […]

Yes, that's expected behaviour (we had a few comments on this on the
list). Compare this to, for example:

function hello () { echo "Hello World!\n"; }
$a->b = 'hello';
$c = $a->b;
$a->b (); // undefined method
$c (); // works

But, as closures implement the __invoke method, it's possible to do
this:

$a->b->__invoke ();

or, of course

call_user_func ($a->b);

(which works with all types of callbacks, even non-closures)

> so do we even want the toString() method?

Personally, I don't care.

> > […]

What would you suggest? Having var_export return the function body for
the closures? But what about bound variables? Well, perhaps in the
future you could even export all the bound variables. But for know to
keep it simple I'd say it's best that closures can't be exported.

> > […]

That's intended behaviour. What would be the clone of a closure?
Especially with bound variables? This would allow for all sorts of weird
behaviour.

Take, for example, the following:

class Foo {
   private $someProperty;

   function getPrinter ($outputDevice) {
     return function ($text) use ($outputDevice) {
       $outputDevice->print ($this->someProperty, $text);
     };
   }
}

How would you clone that? There are two bound variables in this closure:
$this and $outputDevice. Do you clone them both? Do you clone only
$this? Do you clone only $outputDevice? If you only clone the closure
object itself, it won't change the behaviour from simply creating
another reference to it...

If one *really* needs a clonable *and* callable object, write a class
that implements __clone and __invoke. Then the programmer can control
the exact semantics. Everything else would simply be extremely confusing.

And, if I take another language such as Python for example, there you
can't clone closures either, y = copy.deepcopy (closure) returns the
same object.

> > […]
> 
> I guess this is a draw back from the OO approach (rather than the 
> original resource approach), but solvable?

No, the original resource approach was just the same. A resource is
basically an integer value which is then used to look up some specific
data. You can't clone a resource either.

Regards,
Christian

---

## #39183 — Lars Strojny — 2008-07-22T13:06:05+00:00

URL: https://externals.io/message/39172#39183 · In reply to: #39180

Hi Christian,

Am Dienstag, den 22.07.2008, 14:15 +0200 schrieb Christian Seiler:
> 
> > […]
> 
> Yes, that's expected behaviour (we had a few comments on this on the
> list).

Hm, I'm not sure who expected it that way. At least Stas and myself
voted *for* allowing it. We need to discuss the semantics (the order how
methods are resolved, interceptors) but I had the feeling that most of
use really much liked that feature.

cu, Lars

---

## #39196 — Stanislav Malyshev — 2008-07-22T20:08:11+00:00

URL: https://externals.io/message/39172#39196 · In reply to: #39173

Hi!

> so do we even want the toString() method?

IMHO we should drop toString from Closure.

> > […]
> 
> not a huge biggy to me.

I don't think Closure can be meaningfully exported. Can we prohibit it?

> I guess this is a draw back from the OO approach (rather than the 
> original resource approach), but solvable?

I think we can make working clone there - just copy all data, etc.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #39197 — Stanislav Malyshev — 2008-07-22T20:09:32+00:00

URL: https://externals.io/message/39172#39197 · In reply to: #39183

Hi!

> Hm, I'm not sure who expected it that way. At least Stas and myself
> voted *for* allowing it. We need to discuss the semantics (the order how
> methods are resolved, interceptors) but I had the feeling that most of
> use really much liked that feature.

I'm all for doing it, the problem is the syntax $foo->bar() is already 
used. But you can do $foo->bar->__invoke()!
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #39198 — Lars Strojny — 2008-07-22T20:15:03+00:00

URL: https://externals.io/message/39172#39198 · In reply to: #39197

Hi Stas,

Am Dienstag, den 22.07.2008, 13:09 -0700 schrieb Stanislav Malyshev:
[...]
> I'm all for doing it, the problem is the syntax $foo->bar() is already 
> used. But you can do $foo->bar->__invoke()!

Can't we change zend_std_get_method() to return a zend_internal_function
struct in case of a closure on a property? The only thing that needs to
be done would be to define the resolution order which is the trickier
thing.

cu, Lars

---

## #39199 — Marcus Boerger — 2008-07-22T20:48:13+00:00

URL: https://externals.io/message/39172#39199 · In reply to: #39198

Hello Lars,

   actually this is a very good idea and should work :-)

marcus

Tuesday, July 22, 2008, 10:15:03 PM, you wrote:

> Hi Stas,

> Am Dienstag, den 22.07.2008, 13:09 -0700 schrieb Stanislav Malyshev:
> [...]
> > […]

> Can't we change zend_std_get_method() to return a zend_internal_function
> struct in case of a closure on a property? The only thing that needs to
> be done would be to define the resolution order which is the trickier
> thing.

> cu, Lars



Best regards,
 Marcus

---

## #39200 — Marcus Boerger — 2008-07-22T20:50:07+00:00

URL: https://externals.io/message/39172#39200 · In reply to: #39196

Hello Stanislav,

Tuesday, July 22, 2008, 10:08:11 PM, you wrote:

> Hi!

> > […]

> IMHO we should drop toString from Closure.

Sam here. It makes no sense anyway. This mail thread just proved that.

> > […]

> I don't think Closure can be meaningfully exported. Can we prohibit it?

We could add a special case that disallows it.

> > […]

> I think we can make working clone there - just copy all data, etc.


Yep, it should be possible. On the other hand, we probably would like to
change the bound variables then which brings us to having to support
properties. Hey, didn't we just disable that?

Best regards,
 Marcus

---

## #39201 — Stanislav Malyshev — 2008-07-22T21:07:58+00:00

URL: https://externals.io/message/39172#39201 · In reply to: #39198

Hi!

> Can't we change zend_std_get_method() to return a zend_internal_function
> struct in case of a closure on a property? The only thing that needs to

That means that:
1. We can't have properties named same as functions anymore
2. We'd have to check properties every time method name was not found
3. __call will be broken - now should we check properties or go to 
__call when method is not defined?
4. zend_std_get_method should be calling __get too, in case this 
property is not a direct property!

I don't think it's going to work out. Using __invoke is much easier.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #39204 — Marcus Boerger — 2008-07-22T22:44:40+00:00

URL: https://externals.io/message/39172#39204 · In reply to: #39201

Hello Stanislav,

Tuesday, July 22, 2008, 11:07:58 PM, you wrote:

> Hi!

> > […]

> That means that:
> 1. We can't have properties named same as functions anymore

Nope. It means if you have a function named foo and a property foo that
sores a closure and then call foo(), then obviously the function is called
rather than the closure.

> 2. We'd have to check properties every time method name was not found

We could add a flag for this to make it faster. That is whenever someone
sets a property to a closure.

> 3. __call will be broken - now should we check properties or go to 
> __call when method is not defined?

How is it broken? __call does not get called if there is something callable
already.

> 4. zend_std_get_method should be calling __get too, in case this 
> property is not a direct property!

Maybe. However this only applies to overloaded objects. Maybe those cannot
or should not hold closures.

> I don't think it's going to work out. Using __invoke is much easier.
> -- 
> Stanislav Malyshev, Zend Software Architect
> stas@zend.com   http://www.zend.com/
> (408)253-8829   MSN: stas@zend.com




Best regards,
 Marcus

---

## #39205 — Stanislav Malyshev — 2008-07-22T22:54:48+00:00

URL: https://externals.io/message/39172#39205 · In reply to: #39204

Hi!

> Nope. It means if you have a function named foo and a property foo that
> sores a closure and then call foo(), then obviously the function is called
> rather than the closure.

That means you can't call the closure, and nothing alerts you of the 
problem.

> > […]
> 
> We could add a flag for this to make it faster. That is whenever someone
> sets a property to a closure.

Whenever someone sets a property to a closure what happens? Does it mean 
that every call to write_property argument would be checked for 
instanceof Closure? What about write_dimension or get_property_ptr_ptr?

> > […]
> 
> How is it broken? __call does not get called if there is something callable
> already.

__call doesn't work anymore if there's a property with the name that is 
equal to called function. That could be a big surprise for classes that 
use __call for routing.

> Maybe. However this only applies to overloaded objects. Maybe those cannot
> or should not hold closures.

What do you mean by "overloaded objects"? Every object has get_property 
handler.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #39208 — Marcus Boerger — 2008-07-22T23:20:24+00:00

URL: https://externals.io/message/39172#39208 · In reply to: #39205

Hello Stanislav,

Wednesday, July 23, 2008, 12:54:48 AM, you wrote:

> Hi!

> > […]

> That means you can't call the closure, and nothing alerts you of the 
> problem.

> > […]

> Whenever someone sets a property to a closure what happens? Does it mean 
> that every call to write_property argument would be checked for 
> instanceof Closure? What about write_dimension or get_property_ptr_ptr?

> > […]

> __call doesn't work anymore if there's a property with the name that is 
> equal to called function. That could be a big surprise for classes that 
> use __call for routing.

> > […]

> What do you mean by "overloaded objects"? Every object has get_property 
> handler.

Hmmmm, the amount of problems is pretty long. So even though it might sound
cool to do it. It is the better deceision to not allow it. Also you've
shown several paths where it would slow general execution down. Yet I hate
not being able to easily make it work.

Best regards,
 Marcus

---

## #39209 — Stanislav Malyshev — 2008-07-22T23:31:09+00:00

URL: https://externals.io/message/39172#39209 · In reply to: #39208

Hi!

> Hmmmm, the amount of problems is pretty long. So even though it might sound
> cool to do it. It is the better deceision to not allow it. Also you've
> shown several paths where it would slow general execution down. Yet I hate
> not being able to easily make it work.

Well, $foo->bar->__invoke() is not that hard. But if we find a way to 
make it not clash with __get/__call and whole story with properties - 
I'm all for it.
-- 
Stanislav Malyshev, Zend Software Architect
stas@zend.com   http://www.zend.com/
(408)253-8829   MSN: stas@zend.com

---

## #39259 — Christian Seiler — 2008-07-24T09:10:10+00:00

URL: https://externals.io/message/39172#39259 · In reply to: #39196

Hi!

> > […]
> 
> IMHO we should drop toString from Closure.

There is one case which *may* cause problems:

function doSomething ($callback) {
   if (!is_callable ($callback)) {
     throw new Exception (...);
   }

   // special treatment
   if ($callback == 'default') {
     ...
   } else {
     ...
   }
}

Here, the comparison of $callback with 'default' will yield a notice
that $callback couldn't be converted. The comparison will fail
correctly, of course, but it could cause some confusion. The correct
way of comparing would obviously be using ===, that won't cause these
hickups.

Personally, I don't care whether a object-to-string cast is present or
not.

> I don't think Closure can be meaningfully exported. Can we prohibit it? 

Unfortunately, currently not, see the var_export code.

> I think we can make working clone there - just copy all data, etc. 

Perhaps. But I'd recommend this should be postponed, as there are quite
a few subtleties regarding semantics of bound variables involved. Should
there seem to be a strong need for cloning closures by the community,
this can always be implemented in a future PHP version. IMHO.

Regards,
Christian

---

## #39261 — Lukas Kahwe Smith — 2008-07-24T10:05:06+00:00

URL: https://externals.io/message/39172#39261 · In reply to: #39259

> Personally, I don't care whether a object-to-string cast is present or
> not.

So lets remove it?

> > […]
>
> Unfortunately, currently not, see the var_export code.
>
> > […]
>
> Perhaps. But I'd recommend this should be postponed, as there are  
> quite
> a few subtleties regarding semantics of bound variables involved.  
> Should
> there seem to be a strong need for cloning closures by the community,
> this can always be implemented in a future PHP version. IMHO.


I agree that both are not super high priority. So lets keep them for  
5.4/6.0, ok?

regards,
Lukas Kahwe Smith
mls@pooteeweet.org
