Hi,
I am sending this on behalf of Kalle:
- 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.
- Closures can be defined as constants values because of its toString
method:
define('Closure', function(){ echo 'Test'; });
echo constant('Closure'); /* (string) Closure object */
- Since Closures have this toString method, but its not showing up in
Reflection:
Reflection::export(new ReflectionClass('Closure'));
-
var_export()
and Closures is useless, any call tovar_export()
like
the example
below will all be the same:
$lambda = function()
{
echo 'Λ Lambda';
};
var_export($lambda);
/*
Closure::__set_state(array(
))
*/
Maybe it could return some relevant information for exporting the
closure across
data
- 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
- 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.
- Closures can be defined as constants values because of its
toString method:define('Closure', function(){ echo 'Test'; });
echo constant('Closure'); /* (string) Closure object */
- 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?
var_export()
and Closures is useless, any call tovar_export()
like the example
below will all be the same:$lambda = function()
{
echo 'Λ 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.
- 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
Hi Lukas,
- 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.
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.
var_export()
and Closures is useless
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.
- Its impossible to clone a closure using the cloning keyword:
$a = function(){};
$b = clone $a;
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.
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?
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
Hi Christian,
Am Dienstag, den 22.07.2008, 14:15 +0200 schrieb Christian Seiler:
Calling: $a->b(); will result in method A::B() does not exists.
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
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
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
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:
[...]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
Best regards,
Marcus
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:
- We can't have properties named same as functions anymore
- We'd have to check properties every time method name was not found
- __call will be broken - now should we check properties or go to
__call when method is not defined? - 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
Hello Stanislav,
Tuesday, July 22, 2008, 11:07:58 PM, you wrote:
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:
- 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.
- 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.
- __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.
- 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
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'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.
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 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.
__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
Hello Stanislav,
Wednesday, July 23, 2008, 12:54:48 AM, you wrote:
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'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.
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 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.
__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.
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
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
Hi!
so do we even want the toString() method?
IMHO we should drop toString from Closure.
Maybe it could return some relevant information for exporting the
closure across
datanot 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
Hello Stanislav,
Tuesday, July 22, 2008, 10:08:11 PM, you wrote:
Hi!
so do we even want the toString() method?
IMHO we should drop toString from Closure.
Sam here. It makes no sense anyway. This mail thread just proved that.
Maybe it could return some relevant information for exporting the
closure across
datanot a huge biggy to me.
I don't think Closure can be meaningfully exported. Can we prohibit it?
We could add a special case that disallows 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.
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
Hi!
so do we even want the toString() method?
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
Personally, I don't care whether a object-to-string cast is present or
not.
So lets remove it?
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.
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