Good evening,
I am proposing two new RFCs. As they are both inter-related and complementary, with the second having the first as a prerequisite, I’m making just a single email to cover both:
https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencing
Both have written, tested and working patches aimed for master.
Thoughts appreciated, thanks.
Andrea Faulds
http://ajf.me/
Hello Andrea,
from userland perspective, I would prefer to open the Closure constructor instead of adding new syntax:
$qux = new FooBar(3);
// $func = &FooBar::getStatic;
$func = new Closure(array('FooBar', 'getStatic'));
$func($qux); // 3
Regards
Thomas
Andrea Faulds wrote on 03.08.2014 23:50:
Good evening,
I am proposing two new RFCs. As they are both inter-related and complementary,
with the second having the first as a prerequisite, I’m making just a single
email to cover both:https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencingBoth have written, tested and working patches aimed for master.
Thoughts appreciated, thanks.
Andrea Faulds
http://ajf.me/
from userland perspective, I would prefer to open the Closure constructor instead of adding new syntax:
$qux = new FooBar(3);
// $func = &FooBar::getStatic;
$func = new Closure(array('FooBar', 'getStatic'));
$func($qux); // 3
You can actually do that already in a convoluted way:
$func = (new ReflectionMethod(“FooBar::getStatic”)->getClosure());
Though it must be explicitly bound in the case of a non-static method.
--
Andrea Faulds
http://ajf.me/
Hey Andrea,
I really love function referencing RFC, this is something I miss in PHP
and would I have a voting right I'd would +1 even in this state of it.
But I dislike a bit the fact that we start to use Closure for everything,
I really wish we had a dedicated type for functions (read functions as a
first-class citizens).
Good evening,
I am proposing two new RFCs. As they are both inter-related and
complementary, with the second having the first as a prerequisite, I’m
making just a single email to cover both:https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencingBoth have written, tested and working patches aimed for master.
Thoughts appreciated, thanks.
Andrea Faulds
http://ajf.me/
Hi!
This syntax collides with by-ref assignment and by-ref arrays, which is
not good. Different things should not look the same.
I'm not sure also how exactly non-closures can function as closures - if
you take SplFixedArray::getSize and rebind it to SplFileObject, what is
supposed to happen?
I also do not understand why one needs &strlen if "strlen" works just
fine. Only to declare "PHP now has first-class functions"? You can
declare it right now. No need to change the syntax for that, since the
engine does exactly the same and no additional capabilities appear
(though additional problems - e.g. rebinding non-closures - do).
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
Good morning!
Hey Andrea,
I really love function referencing RFC, this is something I miss in PHP and would I have a voting right I'd would +1 even in this state of it.
But I dislike a bit the fact that we start to use Closure for everything, I really wish we had a dedicated type for functions (read functions as a first-class citizens).
I was originally going to go to the effort of adding a function reference class, but then I realised it’d be simpler and make more sense to just use Closure.
To be honest I don’t think it matters much. If it really bothers enough people, we could change the class’s name to Function or something and make Closure an alias, but I don’t see the point.
This syntax collides with by-ref assignment and by-ref arrays, which is
not good. Different things should not look the same.
It doesn’t collide, there is no syntactical ambiguity at an engine level, at least. It does unfortunately look similar, however. The choice of syntax is largely for a lack of better alternatives. It also matches what C and C++ do. I had wanted to do something like ‘function foobar’, but that’s clunky and actually couldn’t be done in the parser. I wondered what Hack does here (since it needs such a syntax to avoid breaking the type checker), but they have a horrible fun(‘foobar’) pseudo-function. Someone, I think it was Anthony, pointed out to me that & wasn’t actually syntactically ambiguous like I’d thought, so I decided to go with that. Since you can’t reference constants, and since this doesn’t support dynamic references, I think it will not be that confusing.
I'm not sure also how exactly non-closures can function as closures - if
you take SplFixedArray::getSize and rebind it to SplFileObject, what is
supposed to happen?
A fatal error, as methods of internal classes can’t be bound to other classes. This is our current behaviour anyway if you try to do that.
I also do not understand why one needs &strlen if "strlen" works just
fine.
The hope is that it’d be more obvious a function is being referenced without needing to know the signature of the function to which the callable is being passed. I suppose it also means you could type hint for Closure instead of callable to guarantee you get an object you can use the Closure methods on.
Only to declare "PHP now has first-class functions"? You can
declare it right now. No need to change the syntax for that, since the
engine does exactly the same and no additional capabilities appear
(though additional problems - e.g. rebinding non-closures - do).
PHP’s functions aren’t first-class, we just have a way to reference them with strings. What we do is similar to what C does with function pointers. An advantage of being able to get normal functions and methods as closures is it means that anything we add to the Closure class can be used on them. For example, if we added built-in partial application (something I’d sure like to see, it’s just difficult to implement), you’d be able to use it on any existing userland or extension function. If we added built-in currying, a related feature, you’d also be able to use it. The syntax means PHP no longer has two classes of functions, essentially.
Is the advantage of
Closure#call()
only performance? Because I don't see other advantages otherwise, and I don't think the performance impact is relevant.
There is a performance advantage which I’m going to test at some point, but that’s not why I propose adding it. It is to simplify binding closures. It’s not an uncommon operation to want to bind at call-time in other languages, and it can be useful.
As for the function referencing RFC, I like it, but it is an annoying syntax that seems to have been built just to accomodate the parser, and that can be worked around in userland with overhead and more typing:
$functionReference = function ($param) { return functionName($param); };
That doesn’t work properly for class methods, both instance and static. It doesn’t even work properly for normal functions, as you’re stripping away parameter information.
A syntax that I'd like, and which is IMO BC compatible would be
::function
:array_map(count::function, [[1, 2], [3, 4]]);
This is also very similar and similarly readable to what we have right now with PHP 5.5 through the
::class
meta-constant.
There's also no way to usefunction
as a constant anywhere, so it should be safe to use that right now.
That syntax isn’t doable in the parser and isn't backwards-compatible anyway. Classes and functions share the same namespace, so what do you do when I have a class called FooBar and a function called FooBar? How about if I have a class with a constant named function? How do you propose to implement this pseudo-class system where functions act like classes in only one case, when fetching this fake class constant? How do you deal with the fact that constants cannot (with very good reason) be objects? The idea is good, it just falls down from a practical standpoint.
Thanks.
Andrea Faulds
http://ajf.me/
Is the advantage of
Closure#call()
only performance? Because I don't
see other advantages otherwise, and I don't think the performance impact is
relevant.There is a performance advantage which I’m going to test at some point,
but that’s not why I propose adding it. It is to simplify binding closures.
It’s not an uncommon operation to want to bind at call-time in other
languages, and it can be useful.
The fact that I'm arguing about is "yet another method to maintain" for an
API that can simply live in userland (and is easier to
maintain/upgrade/etc)...
Unless there's a huge overhead, I don't see a problem with creating new
closures: I do a lot of performance-sensitive stuff with Closure::bind()
and I don't see how this API could help me.
As for the function referencing RFC, I like it, but it is an annoying
syntax that seems to have been built just to accomodate the parser, and
that can be worked around in userland with overhead and more typing:$functionReference = function ($param) { return functionName($param); };
That doesn’t work properly for class methods, both instance and static. It
doesn’t even work properly for normal functions, as you’re stripping away
parameter information.
Why do you actually need parameter information here?
Starting from PHP 5.6, dynamic parameters can be passed on via variadics
anyway. By-ref and by-val is also no big deal as well.
This kind of call can be recognized by static analyzers, and can be
refactored by IDEs.
A syntax that I'd like, and which is IMO BC compatible would
be::function
:array_map(count::function, [[1, 2], [3, 4]]);
This is also very similar and similarly readable to what we have right
now with PHP 5.5 through the::class
meta-constant.
There's also no way to usefunction
as a constant anywhere, so it
should be safe to use that right now.That syntax isn’t doable in the parser and isn't backwards-compatible
anyway. Classes and functions share the same namespace, so what do you do
when I have a class called FooBar and a function called FooBar?
FooBar::methodName::function vs FooBar::function - clear and simple IMO.
Consider http://3v4l.org/SY6vD, http://3v4l.org/YuMZ2 and
http://3v4l.org/9ZRFN
How about if I have a class with a constant named function?
Not allowed by the language
How do you propose to implement this pseudo-class system where functions
act like classes in only one case, when fetching this fake class constant?
I'm not: ::function
is not a constant, it would be recognized by the
parser directly.
How do you deal with the fact that constants cannot (with very good
reason) be objects?
Not sure what you mean with this..
Marco Pivetta
The fact that I'm arguing about is "yet another method to maintain" for an API that can simply live in userland (and is easier to maintain/upgrade/etc)...
Unless there's a huge overhead, I don't see a problem with creating new closures: I do a lot of performance-sensitive stuff withClosure::bind()
and I don't see how this API could help me.
Well, perhaps this API is not aimed at you specifically. The broader question is why shouldn’t we allow binding $this at runtime like we do for any other parameter? Creating a closure only to immediately throw it away is a waste of code and time. Since we can permit this, it is useful, and it doesn’t cause any problems, I don’t see why we shouldn't.
If it’s really such a maintainability problem (I wouldn’t say so, the function’s quite simple), then you can get me to maintain it. After all, I wrote it.
Why do you actually need parameter information here?
Starting from PHP 5.6, dynamic parameters can be passed on via variadics anyway. By-ref and by-val is also no big deal as well.This kind of call can be recognized by static analyzers, and can be refactored by IDEs.
It still has the problem of not allowing re-scoping or arbitrary $this binding. Your solution works only for global functions. It also removes the benefit of knowing at the point of reference whether the function actually exists.
FooBar::methodName::function vs FooBar::function - clear and simple IMO.
It might be clear and simple, but I don’t think it’s realistic. I’m not sure if it’s even possible in the parser, as you’d be fetching a class then suddenly backtracking and deciding you actually want to do something else entirely. Of course, with Nikita’s AST, this probably is possible.
How about if I have a class with a constant named function?
Not allowed by the language
Good point.
How do you propose to implement this pseudo-class system where functions act like classes in only one case, when fetching this fake class constant?
I'm not:
::function
is not a constant, it would be recognized by the parser directly.
Right, but at least under the current parser, you couldn’t do that, to the best of my knowledge. I think using ::function over-complicates things.
That said, it is another option, just not one we could implement immediately or that I personally am particularly fond of.
Andrea Faulds
http://ajf.me/
The fact that I'm arguing about is "yet another method to maintain" for
an API that can simply live in userland (and is easier to
maintain/upgrade/etc)...
Unless there's a huge overhead, I don't see a problem with creating new
closures: I do a lot of performance-sensitive stuff withClosure::bind()
and I don't see how this API could help me.Well, perhaps this API is not aimed at you specifically. The broader
question is why shouldn’t we allow binding $this at runtime like we do
for any other parameter? Creating a closure only to immediately throw it
away is a waste of code and time. Since we can permit this, it is useful,
and it doesn’t cause any problems, I don’t see why we shouldn't.
It is a waste of memory, but I'd first like to see an actual real-world
use-case for it. There are simpler ways to avoid the memory usage, by
allowing stuff like following:
$something = function () use (& $object) {};
Then binding the closure to a specific scope and forgetting about $this
.
That is efficient both memory and cpu-wise.
If it’s really such a maintainability problem (I wouldn’t say so, the
function’s quite simple), then you can get me to maintain it. After all, I
wrote it.
Yes, but upgrading PHP is a mess (from a
business/bureaucracy/devops/whoeverhatesmeatwork perspective. You can ask
that to some other users of this mailing list that solely use the ml to
vent about that.
Upgrading libraries is easy.
FooBar::methodName::function vs FooBar::function - clear and simple IMO.
It might be clear and simple, but I don’t think it’s realistic.
PHP 5.5 introduced the ::class
construct, which I currently use quite a
lot and makes refactoring a breeze. I don't see why this syntax wouldn't
work.
How do you propose to implement this pseudo-class system where functions
act like classes in only one case, when fetching this fake class constant?I'm not:
::function
is not a constant, it would be recognized by the
parser directly.Right, but at least under the current parser, you couldn’t do that, to the
best of my knowledge. I think using ::function over-complicates things.
No clue how complicated Foo::bar::function
gets with the current parser,
but if that's the problem, then I'd ask Nikic directly, as he may find an
easy solution if the AST-based parsing makes it into core.
That said, it is another option, just not one we could implement
immediately or that I personally am particularly fond of.
My point here is consistency. I'd love to have the same kind of static
reference for classes and functions/methods. Even ::const
eventually, as
well as other symbols if anyone can think of them.
Marco Pivetta
Hi!
It doesn’t collide, there is no syntactical ambiguity at an engine
level, at least. It does unfortunately look similar, however. The
That's the collision. It looks exactly the same, but does entirely
different thing. This is not good.
choice of syntax is largely for a lack of better alternatives. It
also matches what C and C++ do. I had wanted to do something like
It doesn't match what C and C++ do - it does a completely different
thing from what C and C++ do. It's not an address and it's not a reference.
The hope is that it’d be more obvious a function is being referenced
without needing to know the signature of the function to which the
I don't understand how for writing "strlen" you need to know its signature.
callable is being passed. I suppose it also means you could type hint
for Closure instead of callable to guarantee you get an object you
can use the Closure methods on.
Didn't we add "callable" exactly for identifying callables? So now
callable is not enough and we need also typing by Closure? But wait,
callable is still supported for calling, so what you want to do with
Closure? Right now it has two methods - for binding - and both are not
guaranteed to work at all - as you just said, it would fail to work on
any internal function and on most non-internal functions too, since they
have no idea what to do when bound. So you get an object that actually
guarantees nothing at all.
PHP’s functions aren’t first-class, we just have a way to reference
them with strings. What we do is similar to what C does with function
In what meaning they are not first-class that your change is making them
first-class?
pointers. An advantage of being able to get normal functions and
methods as closures is it means that anything we add to the Closure
class can be used on them. For example, if we added built-in partial
But it can not. E.g. you can not bind such things. They can not have
environments. They only thing you can do with them is call them - but
you can do it right now!
application (something I’d sure like to see, it’s just difficult to
implement), you’d be able to use it on any existing userland or
You can do partial application to any callable just now by generating an
anon function that applies it via call_use_function. The only thing
introducing Closure there would change is how the method being called is
named.
extension function. If we added built-in currying, a related feature,
you’d also be able to use it. The syntax means PHP no longer has two
classes of functions, essentially.
We don't have two classes of functions right now. If you go to engine
level, we have more than that, if you stay on PHP level, you just have
callable, which is not really a function but rather a way to address a
function.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
It doesn’t collide, there is no syntactical ambiguity at an engine
level, at least. It does unfortunately look similar, however. TheThat's the collision. It looks exactly the same, but does entirely
different thing. This is not good.
It doesn’t look exactly the same, you can’t reference a constant.
It doesn't match what C and C++ do - it does a completely different
thing from what C and C++ do. It's not an address and it's not a reference.
In C and C++ you get a function pointer. Here you get a closure. In both cases you get something you can call, pass as an argument and return.
The hope is that it’d be more obvious a function is being referenced
without needing to know the signature of the function to which theI don't understand how for writing "strlen" you need to know its signature.
callable is being passed.
I said "the signature of the function to which the callable is being passed”. If I do this:
foo(“foobar”);
I don’t know if foo takes a callable or it takes a string. It’s not clear, without knowing foo’s signature, what we’re trying to give it. However, this is clearer:
foo(&foobar);
In this case, if we know of the & syntax, it is clear a function is being passed.
So now
callable is not enough and we need also typing by Closure? But wait,
callable is still supported for calling, so what you want to do with
Closure? Right now it has two methods - for binding - and both are not
guaranteed to work at all - as you just said, it would fail to work on
any internal function and on most non-internal functions too, since they
have no idea what to do when bound. So you get an object that actually
guarantees nothing at all.
Well, people can always write broken code that passes nonsensical arguments. Equally at the moment, if someone asks for a comparison function, I can pass strlen is I really want to. I don’t see what your point is. Closures have methods that can be used, strings do not. Yes, this doesn’t work for some closures. I can pass invalid arguments to other methods too.
In what meaning they are not first-class that your change is making them
first-class?
Actually, arguably, PHP’s functions currently are first-class as they full fill the three main requirements if you use strings (or arrays for methods). They can be passed as arguments, returned, and assigned to variables, or at least strings and arrays can be. My apologies.
But it can not. E.g. you can not bind such things. They can not have
environments. They only thing you can do with them is call them - but
you can do it right now!
You can actually bind a user function, it’s just not useful for most user functions which don’t make use of $this. Although if you really want to, you can write one which does. You could already do this, and it would work perfectly.
You can do partial application to any callable just now by generating an
anon function that applies it via call_use_function. The only thing
introducing Closure there would change is how the method being called is
named.
Right, but you could add an internal method to do partial application for you.
We don't have two classes of functions right now. If you go to engine
level, we have more than that, if you stay on PHP level, you just have
callable, which is not really a function but rather a way to address a
function.
We do have two classes of functions from a userland perspective: Closures and functions/methods. The former are passed around as objects with methods, the latter are passed around with strings or arrays.
--
Andrea Faulds
http://ajf.me/
Hi!
It doesn’t look exactly the same, you can’t reference a constant.
That's why it is bad - because it looks like you're referencing a
constant, but in fact it does something completely unrelated.
In C and C++ you get a function pointer. Here you get a closure. In
both cases you get something you can call, pass as an argument and
return.
You can pass and return anything. But it C and C++, & means physical
address of an entity - the place in memory where it is stored. In your
syntax, it means creating a new object that contains some code that if
some method of this object is invoked it would call the function. It's
not even close to being the same - the only similarity is that both can
be passed as argument, which can also be said about literally every
other value in the language.
I don’t know if foo takes a callable or it takes a string. It’s not
clear, without knowing foo’s signature, what we’re trying to give it.
However, this is clearer:foo(&foobar);
In real life, it'd be foo($foobar) and your argument would not work
anyway. Function types are never obvious from function call, that's why
people have docs and IDEs. Adding new syntax just to say "this means
callable" is not a good idea, if you really really need it in your code,
just comment it. Or use an IDE which would tell you foo's signature.
This is so narrow case that I can't really see how it can justify new
syntax, even more - one that collides with existing one.
function, I can pass strlen is I really want to. I don’t see what
your point is. Closures have methods that can be used, strings do
My point is that this syntax does not add anything we can't do now, is
confusing by colliding with existing syntax, based on Closure while we
in advance know no existing methods of Closure would work on it and
promises to provide "first class functions" where in fact not adding
anything that doesn't exist right now.
Right, but you could add an internal method to do partial application
for you.
You could create such method right now, you don't need new syntax for
that. It also would work on any function, not just on special-syntax ones.
We do have two classes of functions from a userland perspective:
Closures and functions/methods. The former are passed around as
objects with methods, the latter are passed around with strings or
arrays.
The fact that closures have methods is an implementation detail, which
is important only if you want to rebind them. Which is exactly where
your case would produce fatal errors. But if you really want to wrap
everything into Closure, just make a function that does:
function closureify($callable) {
return function(...$args) use($callable) {
call_user_func_array($callable, $args); }
}
It's an one-liner, though its usefulness is doubtful for me.
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/
Good evening,
I am proposing two new RFCs. As they are both inter-related and
complementary, with the second having the first as a prerequisite, I’m
making just a single email to cover both:https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencingBoth have written, tested and working patches aimed for master.
Thoughts appreciated, thanks.
Andrea Faulds
http://ajf.me/
Hey Andrea,
Is the advantage of Closure#call()
only performance? Because I don't see
other advantages otherwise, and I don't think the performance impact is
relevant.
As for the function referencing RFC, I like it, but it is an annoying
syntax that seems to have been built just to accomodate the parser, and
that can be worked around in userland with overhead and more typing:
$functionReference = function ($param) { return functionName($param); };
A syntax that I'd like, and which is IMO BC compatible would be::function
:
array_map(count::function, [[1, 2], [3, 4]]);
This is also very similar and similarly readable to what we have right now
with PHP 5.5 through the ::class
meta-constant.
There's also no way to use function
as a constant anywhere, so it should
be safe to use that right now.
Thoughts?
Andrea + Marco
Is the advantage of
Closure#call()
only performance? Because I don't see
other advantages otherwise, and I don't think the performance impact is
relevant.
I like the proposal, but with some modifications. I am unsure if there
is another RFC out there on function de-referencing, but at current the
following is not possible:
$x = function() { return function () { echo 'hi'; }; };
$x()();
Nor is de-referencing of a closure assigned to an object property:
class Foo { public $bar; }
$f = new Foo;
$f->bar = function () { echo 'hi'; };
$f->bar();
While those are less important on their own, the use case that this does
address is the ability to inline call a closure assigned to an
object(/or class) property and fluently without call_user_func:
class Foo { public $bar; }
$f = new Foo;
$f->bar = function () {
echo 'hi'; return function () {
echo ' world';
};
};
call_user_func(call_user_func($f->bar));
"->call()" would facilitate the following syntax, which I'd argue has
greater semantic meaning than the call_user_func()
variant:
$f->bar->call()->call();
Additionally, the above assumes the following modification to the RFC:
the additional of bindCall(), or something similarly named. The purpose
here is to be able to call(/* args /) an existing closure with the
current bound context (as a result from a previous bindTo) and
bindCall($context, / args */); to support the bind-and-call workflow.
The reason for call() to operate on existing bound context would be this
workflow:
class Foo {
public $bar;
public function registerBar(\Closure $c) {
// bind to specific scope as per Foo's specifications
$this->bar = $c->bindTo(...);
}
}
$f = new Foo;
$f->registerBar(function () {
echo 'hi'; return function () {
echo ' world';
};
});
$f->bar->call();
// if you must change context:
$f->bar->bindCall($otherContext);
Thanks,
Ralph Schindler
"->call()" would facilitate the following syntax, which I'd argue has greater semantic meaning than the
call_user_func()
variant:$f->bar->call()->call();
At the risk of stating the obvious, can’t you just use $f->bar->__invoke()?
Andrea Faulds
http://ajf.me/
"->call()" would facilitate the following syntax, which I'd argue has
greater semantic meaning than thecall_user_func()
variant:$f->bar->call()->call();
At the risk of stating the obvious, can’t you just use $f->bar->__invoke()
You can't do that if you can't assume that bar
is a Closure
instance.
Marco Pivetta
Hi Andrea,
Could you measure the performance impact of function referencing.
<?php
$func = "strlen";
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>
vs
<?php
$func = &strlen;
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>
I don't like the "&" syntax a lot, but I understand that it's a compromise
between readability and implementation complication.
The patch probably misses support for "&self::foo", "&parent::foo",
"&static::foo".
It also may be improved using run_time cache (this is not important now).
Thanks. Dmitry.
Good evening,
I am proposing two new RFCs. As they are both inter-related and
complementary, with the second having the first as a prerequisite, I’m
making just a single email to cover both:https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencingBoth have written, tested and working patches aimed for master.
Thoughts appreciated, thanks.
Andrea Faulds
http://ajf.me/
Hi Andrea,
Could you measure the performance impact of function referencing.
<?php
$func = "strlen";
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>vs
<?php
$func = &strlen;
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>
On my machine:
andreas-air:php-src ajf$ time sapi/cli/php ../funcref_a.php
real 0m0.043s
user 0m0.022s
sys 0m0.008s
andreas-air:php-src ajf$ time sapi/cli/php ../funcref_b.php
real 0m0.023s
user 0m0.015s
sys 0m0.006s
I think the reason that function references are faster here is that it only has to do the hash table lookup once. If you tried to create a reference on each iteration and compared that to just using the string, then the string would be faster, as no memory needs to be allocated and deallocated for the closure.
I don't like the "&" syntax a lot, but I understand that it's a compromise between readability and implementation complication.
Right. Myself, I’d prefer to just merge PHP’s namespaces and have $foo = func_name; work, but that wouldn’t work well and would break a lot of things.
The patch probably misses support for "&self::foo", "&parent::foo", "&static::foo”.
Good catch, I’ll try and get that implemented. Actually, I wonder if having &self::foo be bound to the object using it would be a good idea. While I resisted for &$foo::bar (I don’t support that syntax anyway), there might be some merit in it after all. If it would bind for you, you could always remove the binding or change it if it would be a problem.
It also may be improved using run_time cache (this is not important now).
That’s something I wanted to do (might solve the performance problems with using & on each iteration), but I’m not sure quite what the right way to implement that is. In particular, what kind of cache (some sort of hash map?), how to index it (function name? fully-qualified function name?) and where to put it. I suppose I could just add a zval pointer to zend_function itself, but that would add eight bytes to every single function, which would add up.
Actually, on a related note: Currently & creates a new closure on each use, and in PHP closures are compared by identity, not by their value. Perhaps we should change that so it checks for the same scope, this_ptr and implementation? Having &strlen === &strlen would probably be a good thing, it’s the more intuitive thing at least. Comparing the this_ptr of the two would be easy, however I have no idea whether you could compare the two zend_functions and have it work properly. You can’t just do an == in the C code, as closures embed and modify the zend_function.
Andrea Faulds
http://ajf.me/
Hi Andrea,
Could you measure the performance impact of function referencing.
<?php
$func = "strlen";
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>vs
<?php
$func = &strlen;
for ($i = 0; $i < 10000; $i++) {
$func("hello");
}
?>On my machine:
andreas-air:php-src ajf$ time sapi/cli/php ../funcref_a.php real 0m0.043s user 0m0.022s sys 0m0.008s andreas-air:php-src ajf$ time sapi/cli/php ../funcref_b.php real 0m0.023s user 0m0.015s sys 0m0.006s
I think the reason that function references are faster here is that it
only has to do the hash table lookup once. If you tried to create a
reference on each iteration and compared that to just using the string,
then the string would be faster, as no memory needs to be allocated and
deallocated for the closure.I don't like the "&" syntax a lot, but I understand that it's a
compromise between readability and implementation complication.Right. Myself, I’d prefer to just merge PHP’s namespaces and have $foo =
func_name; work, but that wouldn’t work well and would break a lot of
things.
may be:
$a = function strlen;
or
$a = function(stren);
but these are not excellent as well :(
The patch probably misses support for "&self::foo", "&parent::foo",
"&static::foo”.Good catch, I’ll try and get that implemented. Actually, I wonder if
having &self::foo be bound to the object using it would be a good idea.
While I resisted for &$foo::bar (I don’t support that syntax anyway), there
might be some merit in it after all. If it would bind for you, you could
always remove the binding or change it if it would be a problem.It also may be improved using run_time cache (this is not important now).
That’s something I wanted to do (might solve the performance problems with
using & on each iteration), but I’m not sure quite what the right way to
implement that is. In particular, what kind of cache (some sort of hash
map?), how to index it (function name? fully-qualified function name?) and
where to put it. I suppose I could just add a zval pointer to zend_function
itself, but that would add eight bytes to every single function, which
would add up.Actually, on a related note: Currently & creates a new closure on each
use, and in PHP closures are compared by identity, not by their value.
Perhaps we should change that so it checks for the same scope, this_ptr and
implementation? Having &strlen === &strlen would probably be a good thing,
it’s the more intuitive thing at least. Comparing the this_ptr of the two
would be easy, however I have no idea whether you could compare the two
zend_functions and have it work properly. You can’t just do an == in the C
code, as closures embed and modify the zend_function.
I may implement this part if the RFC will be accepted.
Actually, most of the code may be just copy-pasted from
ZEND_INIT_STATIC_METHOD_CALL.
Thanks. Dmitry.
--
Andrea Faulds
http://ajf.me/
may be:
$a = function strlen;
or
$a = function(stren);
but these are not excellent as well :(
I wanted to do the first, but it caused a shift/reduce conflict in the parser due to ambiguity with function () {}. The latter has been suggested also. Both might be possible with an AST, but I’m not really keen on either, they’re quite verbose.
I may implement this part if the RFC will be accepted.
Actually, most of the code may be just copy-pasted from
ZEND_INIT_STATIC_METHOD_CALL.
Interesting, I’d certainly appreciate it. :) One thing to note is that the patch is currently implemented in the simplest way possible, wherein it just stores constant strings in the opcode. You could optimise it by binding ahead-of-time and passing the zend_function pointer itself, but I didn’t want to do that as it complicated things.
--
Andrea Faulds
http://ajf.me/
We didn't keep pointers to functions in opcodes (however it possible in
some cases).
Instead we use cache_slots in op_array->run_time_cache array to keep
pointers to classes and functions.
Anyway, it must be quite easy to extend the patch.
Thanks. Dmitry.
may be:
$a = function strlen;
or
$a = function(stren);
but these are not excellent as well :(
I wanted to do the first, but it caused a shift/reduce conflict in the
parser due to ambiguity with function () {}. The latter has been suggested
also. Both might be possible with an AST, but I’m not really keen on
either, they’re quite verbose.I may implement this part if the RFC will be accepted.
Actually, most of the code may be just copy-pasted from
ZEND_INIT_STATIC_METHOD_CALL.Interesting, I’d certainly appreciate it. :) One thing to note is that the
patch is currently implemented in the simplest way possible, wherein it
just stores constant strings in the opcode. You could optimise it by
binding ahead-of-time and passing the zend_function pointer itself, but I
didn’t want to do that as it complicated things.--
Andrea Faulds
http://ajf.me/
How about 'new Function()'?
Might be a WTF that it creates an instance of Closure, though.
We didn't keep pointers to functions in opcodes (however it possible in
some cases).
Instead we use cache_slots in op_array->run_time_cache array to keep
pointers to classes and functions.
Anyway, it must be quite easy to extend the patch.Thanks. Dmitry.
may be:
$a = function strlen;
or
$a = function(stren);
but these are not excellent as well :(
I wanted to do the first, but it caused a shift/reduce conflict in the
parser due to ambiguity with function () {}. The latter has been
suggested
also. Both might be possible with an AST, but I’m not really keen on
either, they’re quite verbose.I may implement this part if the RFC will be accepted.
Actually, most of the code may be just copy-pasted from
ZEND_INIT_STATIC_METHOD_CALL.Interesting, I’d certainly appreciate it. :) One thing to note is that
the
patch is currently implemented in the simplest way possible, wherein it
just stores constant strings in the opcode. You could optimise it by
binding ahead-of-time and passing the zend_function pointer itself, but I
didn’t want to do that as it complicated things.--
Andrea Faulds
http://ajf.me/
it's ambiguous with regular object creation.
Thanks. Dmitry.
How about 'new Function()'?
Might be a WTF that it creates an instance of Closure, though.
We didn't keep pointers to functions in opcodes (however it possible in
some cases).
Instead we use cache_slots in op_array->run_time_cache array to keep
pointers to classes and functions.
Anyway, it must be quite easy to extend the patch.Thanks. Dmitry.
may be:
$a = function strlen;
or
$a = function(stren);
but these are not excellent as well :(
I wanted to do the first, but it caused a shift/reduce conflict in the
parser due to ambiguity with function () {}. The latter has been
suggested
also. Both might be possible with an AST, but I’m not really keen on
either, they’re quite verbose.I may implement this part if the RFC will be accepted.
Actually, most of the code may be just copy-pasted from
ZEND_INIT_STATIC_METHOD_CALL.Interesting, I’d certainly appreciate it. :) One thing to note is that
the
patch is currently implemented in the simplest way possible, wherein it
just stores constant strings in the opcode. You could optimise it by
binding ahead-of-time and passing the zend_function pointer itself, but
I
didn’t want to do that as it complicated things.--
Andrea Faulds
http://ajf.me/
Good evening,
I am proposing two new RFCs. As they are both inter-related and complementary, with the second having the first as a prerequisite, I’m making just a single email to cover both:
https://wiki.php.net/rfc/closure_apply
https://wiki.php.net/rfc/function_referencing
I am withdrawing the function referencing RFC. I think the syntax is too ambiguous and it doesn’t really solve anything well. The confusion it would create is not worth the benefit.
--
Andrea Faulds
http://ajf.me/