Hi internals!
We have recently been reviewing the interaction between
ReflectionFunctionAbstract::getClosure(), a mechanism which converts an
ordinary function or method into a "fake" closure, and closure rebinding
using Closure::bindTo() and Closure::call().
It turns out that this combination has not yet received testing and
multiple crashes and leaks were found and fixed [1] [2] [3] [4].
We have one last outstanding changeset [5] waiting to land, which we want
to check back with internals first, as it constitutes a BC break late in
the PHP 7.0 release cycle.
This changeset forbids rebinding the scope of closures returned by
getClosure() completely. The background of this change is that PHP 7
includes optimizations that early-bind the scope of self:: during
compilation (and there are more optimizations of this nature pending to
land in PHP 7.1). This means that attempts to change the meaning of "self"
in an ordinary method at runtime will not always succeed and lead to
inconsistent results.
Based on feedback received on an earlier version of this patch [6] [7], we
have not added any additional restrictions on rebinding of $this.
getClosure()s of userland methods can still be bound to arbitrary $this
values and getClosure()s of internal methods can still be bound to
"compatible" $this values. (If someone sees a problem with allowing the
former, please speak up!)
Note that all this applies to ReflectionFunctionAbstract::getClosure()
only. Rebinding behavior of ordinary closures doesn't change.
It would be appreciated if some more people familiar with the closure
implementation could take a look at our rebinding rules and see if there's
any situations that aren't covered yet and could be problematic.
Thanks,
Nikita
[1] https://bugs.php.net/bug.php?id=70630
[2] https://bugs.php.net/bug.php?id=70674
[3] https://bugs.php.net/bug.php?id=70681
[4] https://bugs.php.net/bug.php?id=70685
[5] https://github.com/php/php-src/pull/1560
[6]
https://github.com/php/php-src/commit/517b5536259ecf7697f353f4bfbafde857fc1f81
[7]
https://github.com/php/php-src/commit/881c50252066132f83e190325e344f532be19033
Hi Nikita,
Nikita Popov wrote:
We have recently been reviewing the interaction between
ReflectionFunctionAbstract::getClosure(), a mechanism which converts an
ordinary function or method into a "fake" closure, and closure rebinding
using Closure::bindTo() and Closure::call().It turns out that this combination has not yet received testing and
multiple crashes and leaks were found and fixed [1] [2] [3] [4].
In hindsight, it is probably my fault that this wasn't spotted sooner. I
should've considered the case of ::getClosure() when I wrote
Closure::call().
We have one last outstanding changeset [5] waiting to land, which we want
to check back with internals first, as it constitutes a BC break late in
the PHP 7.0 release cycle.This changeset forbids rebinding the scope of closures returned by
getClosure() completely.
This sounds like a reasonable approach to dealing with the problem. We
already have some restrictions with internal function Closures anyway, I
don't think this will hurt much, especially since cases where you need
to rebind methods into different scopes are quite rare.
So, +1 from me.
Thanks.
Andrea Faulds
http://ajf.me/
Based on feedback received on an earlier version of this patch [6] [7], we
have not added any additional restrictions on rebinding of $this.
getClosure()s of userland methods can still be bound to arbitrary $this
values and getClosure()s of internal methods can still be bound to
"compatible" $this values. (If someone sees a problem with allowing the
former, please speak up!)Note that all this applies to ReflectionFunctionAbstract::getClosure()
only. Rebinding behavior of ordinary closures doesn't change.
A user-defined closure can be rebound freely, but not one created from
Reflection - is that correct?
My personal opinion is that we should be working to eliminate
differences between internal and user functions (including closures).
In your opinion can we make user and internal closures act more alike?
I'm assuming rebind internal methods can't be done properly because
internal objects may use different C structs than a plain zend_object
(hence the crashes). Is that correct?
Am 11.10.2015 um 00:16 schrieb Levi Morrison levim@php.net:
Based on feedback received on an earlier version of this patch [6] [7], we
have not added any additional restrictions on rebinding of $this.
getClosure()s of userland methods can still be bound to arbitrary $this
values and getClosure()s of internal methods can still be bound to
"compatible" $this values. (If someone sees a problem with allowing the
former, please speak up!)Note that all this applies to ReflectionFunctionAbstract::getClosure()
only. Rebinding behavior of ordinary closures doesn't change.A user-defined closure can be rebound freely, but not one created from
Reflection - is that correct?
Yes, correct.
My personal opinion is that we should be working to eliminate
differences between internal and user functions (including closures).
In your opinion can we make user and internal closures act more alike?
I'm assuming rebind internal methods can't be done properly because
internal objects may use different C structs than a plain zend_object
(hence the crashes). Is that correct?
It's rather about the additional meta-data before the object which the internal object requires. Hence you usually can use internal objects like normal zend_object *'s, but not the inverse.
The only way to make them actually more alike is putting more restrictions (and BC breaks) than necessary onto the binding rules. Loosening internal binding rules doesn't work as well...
Bob
Hi Nikita,
I think the effect of the remaining patch is negligible.
It disables things, that shouldn't work by design, didn't work in fact and
leaded to crashes.
It might work only in some cases and only because of luck.
So, I think it's better to commit this before next RC.
Anatol is going to start rolling it on Tuesday.
He will also increase all API version numbers.
It would be great, if we stop any commits into PHP-7.0 except for critical
fixes now
Thanks. Dmitry.
Hi internals!
We have recently been reviewing the interaction between
ReflectionFunctionAbstract::getClosure(), a mechanism which converts an
ordinary function or method into a "fake" closure, and closure rebinding
using Closure::bindTo() and Closure::call().It turns out that this combination has not yet received testing and
multiple crashes and leaks were found and fixed [1] [2] [3] [4].We have one last outstanding changeset [5] waiting to land, which we want
to check back with internals first, as it constitutes a BC break late in
the PHP 7.0 release cycle.This changeset forbids rebinding the scope of closures returned by
getClosure() completely. The background of this change is that PHP 7
includes optimizations that early-bind the scope of self:: during
compilation (and there are more optimizations of this nature pending to
land in PHP 7.1). This means that attempts to change the meaning of "self"
in an ordinary method at runtime will not always succeed and lead to
inconsistent results.Based on feedback received on an earlier version of this patch [6] [7], we
have not added any additional restrictions on rebinding of $this.
getClosure()s of userland methods can still be bound to arbitrary $this
values and getClosure()s of internal methods can still be bound to
"compatible" $this values. (If someone sees a problem with allowing the
former, please speak up!)Note that all this applies to ReflectionFunctionAbstract::getClosure()
only. Rebinding behavior of ordinary closures doesn't change.It would be appreciated if some more people familiar with the closure
implementation could take a look at our rebinding rules and see if there's
any situations that aren't covered yet and could be problematic.Thanks,
Nikita[1] https://bugs.php.net/bug.php?id=70630
[2] https://bugs.php.net/bug.php?id=70674
[3] https://bugs.php.net/bug.php?id=70681
[4] https://bugs.php.net/bug.php?id=70685
[5] https://github.com/php/php-src/pull/1560
[6]
https://github.com/php/php-src/commit/517b5536259ecf7697f353f4bfbafde857fc1f81
[7]
https://github.com/php/php-src/commit/881c50252066132f83e190325e344f532be19033
Hi Nikita,
I think the effect of the remaining patch is negligible.
It disables things, that shouldn't work by design, didn't work in fact and
leaded to crashes.
It might work only in some cases and only because of luck.So, I think it's better to commit this before next RC.
Anatol is going to start rolling it on Tuesday.
Done.
It would be great, if we stop any commits into PHP-7.0 except for
critical fixes now
Maybe keep PHP-7.0 open (or as open as release branches usually are), but
from now on only cherry-pick critical fixes into PHP-7.0.0 (instead of
merging everything)?
Nikita
Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds ajf@ajf.me; Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually are), but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of merging
everything)?
I commit myself to Dmitry's words. What matters today and especially after RC5 is the stability. Today we should invest into testing and bug fixes more than into improvements (aka fixes to something that is not broken). It really matters for the quality of the final. That's the message to convey probably.
Regards
Anatol
On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski anatol.php@belski.net
wrote:
Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of merging
everything)?I commit myself to Dmitry's words. What matters today and especially after
RC5 is the stability. Today we should invest into testing and bug fixes
more than into improvements (aka fixes to something that is not broken). It
really matters for the quality of the final. That's the message to convey
probably.
To rephrase my question: Should we treat PHP-7.0 the same way we treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are okay,
even if it's just improving a test or fixing some inconsequential
behavior), or do you want to limit the PHP-7.0 branch to actually critical
fixes now? From what you say, I assume the former rather than the latter?
Nikita
On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of
merging
everything)?I commit myself to Dmitry's words. What matters today and especially
after RC5 is the stability. Today we should invest into testing and bug
fixes more than into improvements (aka fixes to something that is not
broken). It really matters for the quality of the final. That's the message
to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are okay,
even if it's just improving a test or fixing some inconsequential
behavior), or do you want to limit the PHP-7.0 branch to actually critical
fixes now? From what you say, I assume the former rather than the latter?
Of course, we should merge bug fixes from PHP-5.6.
Delaying them by month will make more overhead for everyone.
But the less changes we will get the better.
Thanks. Dmitry.
Nikita
-----Original Message-----
From: Dmitry Stogov [mailto:dmitry@zend.com]
Sent: Monday, October 12, 2015 11:55 PM
To: Nikita Popov nikita.ppv@gmail.com
Cc: Anatol Belski anatol.php@belski.net; PHP internals
internals@lists.php.net
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()On Mon, Oct 12, 2015 at 11:32 PM, Nikita Popov nikita.ppv@gmail.com
wrote:On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski
anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds
ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created
by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually
are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of
merging
everything)?I commit myself to Dmitry's words. What matters today and especially
after RC5 is the stability. Today we should invest into testing and
bug fixes more than into improvements (aka fixes to something that is
not broken). It really matters for the quality of the final. That's
the message to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are
okay, even if it's just improving a test or fixing some
inconsequential behavior), or do you want to limit the PHP-7.0 branch
to actually critical fixes now? From what you say, I assume the former rather
than the latter?Of course, we should merge bug fixes from PHP-5.6.
Delaying them by month will make more overhead for everyone.
But the less changes we will get the better.
Yeah, the git workflow persists. Of course, it can potentially introduce instabilities. Then cherry-picking is not to sidestep, so in worst case there is a solution anyway.
Regards
Anatol
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 10:33 PM
To: Anatol Belski anatol.php@belski.net
Cc: Dmitry Stogov dmitry@zend.com; PHP internals internals@lists.php.net
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds
ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually
are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of
merging everything)?I commit myself to Dmitry's words. What matters today and especially
after
RC5 is the stability. Today we should invest into testing and bug
fixes more than into improvements (aka fixes to something that is not
broken). It really matters for the quality of the final. That's the
message to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are okay, even if
it's just improving a test or fixing some inconsequential behavior), or do you
want to limit the PHP-7.0 branch to actually critical fixes now? From what you
say, I assume the former rather than the latter?
Talking about "critical" I meant usual definitions as something that causes memory corruptions, data loss, big functional impact, security flaws, etc.
The tricky point with 7.0 right now is that it's a lot of new stuff with very high expectations standing short before final. Coupling this with the "critical" from above, the definition I would make were - it is a) critical and b) can be fixed properly and cannot wait until after the final release. The dev time spent to fix something after 7.0 final is indeed lost for the 7.0 final. Any new code short before final increases the instability risk of the final.
Fe small functional regressions are probably not always critical. If a (even small) functional regression breaks a lot, it is critical. If a functional regression breaks a rare use case - it is not critical. If improving a test helps to cover some critical code - so yes, it is critical as well. Anything that is critical can involve anything you've mentioned like adding tests or code.
Hopefully I could express myself better now. Cherry-pick is of course a solution, but IMHO it is important every dev to understand the unique situation we currently have to face. It is better to avoid cherry-picking in favor of the "mission aware" code :)
Regards
Anatol
Hey:
On Tue, Oct 13, 2015 at 6:12 AM, Anatol Belski anatol.php@belski.net
wrote:
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 10:33 PM
To: Anatol Belski anatol.php@belski.net
Cc: Dmitry Stogov dmitry@zend.com; PHP internals <
internals@lists.php.net>
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds
ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually
are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead of
merging everything)?I commit myself to Dmitry's words. What matters today and especially
after
RC5 is the stability. Today we should invest into testing and bug
fixes more than into improvements (aka fixes to something that is not
broken). It really matters for the quality of the final. That's the
message to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are
okay, even if
it's just improving a test or fixing some inconsequential behavior), or
do you
want to limit the PHP-7.0 branch to actually critical fixes now? From
what you
say, I assume the former rather than the latter?Talking about "critical" I meant usual definitions as something that
causes memory corruptions, data loss, big functional impact, security
flaws, etc.
agree, bugs
The tricky point with 7.0 right now is that it's a lot of new stuff with
very high expectations standing short before final. Coupling this with the
"critical" from above, the definition I would make were - it is a) critical
and b) can be fixed properly and cannot wait until after the final release.
The dev time spent to fix something after 7.0 final is indeed lost for the
7.0 final. Any new code short before final increases the instability risk
of the final.Fe small functional regressions are probably not always critical. If a
(even small) functional regression breaks a lot, it is critical. If a
functional regression breaks a rare use case - it is not critical. If
improving a test helps to cover some critical code - so yes, it is critical
as well. Anything that is critical can involve anything you've mentioned
like adding tests or code.
hmm, I understand your ideas, but I think maybe we should forbid such
things as well for this moment, because it make things complicated to
judge(if it is critical or not), such things can goes to 7.1
let's only allows bugs fix (as you mentioned above) to be committed into
PHP-7.0 before the final release of its
thanks
Hopefully I could express myself better now. Cherry-pick is of course a
solution, but IMHO it is important every dev to understand the unique
situation we currently have to face. It is better to avoid cherry-picking
in favor of the "mission aware" code :)
Regards
Anatol
--
--
Xinchen Hui
@Laruence
http://www.laruence.com/
Hi,
-----Original Message-----
From: Xinchen Hui [mailto:laruence@php.net]
Sent: Tuesday, October 13, 2015 4:23 AM
To: Anatol Belski anatol.php@belski.net
Cc: Nikita Popov nikita.ppv@gmail.com; Dmitry Stogov dmitry@zend.com;
PHP internals internals@lists.php.net
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()Hey:
On Tue, Oct 13, 2015 at 6:12 AM, Anatol Belski anatol.php@belski.net
wrote:-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 10:33 PM
To: Anatol Belski anatol.php@belski.net
Cc: Dmitry Stogov dmitry@zend.com; PHP internals <
internals@lists.php.net>
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures
created by
ReflectionFunctionAbstract::getClosure()On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski
anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds
ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand
bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures
created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except
for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually
are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead
of merging everything)?I commit myself to Dmitry's words. What matters today and
especially after
RC5 is the stability. Today we should invest into testing and bug
fixes more than into improvements (aka fixes to something that is
not broken). It really matters for the quality of the final.
That's the message to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we
treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are
okay, even if
it's just improving a test or fixing some inconsequential behavior),
or
do you
want to limit the PHP-7.0 branch to actually critical fixes now?
From
what you
say, I assume the former rather than the latter?Talking about "critical" I meant usual definitions as something that
causes memory corruptions, data loss, big functional impact, security
flaws, etc.agree, bugs
The tricky point with 7.0 right now is that it's a lot of new stuff
with very high expectations standing short before final. Coupling this
with the "critical" from above, the definition I would make were - it
is a) critical and b) can be fixed properly and cannot wait until after the final
release.
The dev time spent to fix something after 7.0 final is indeed lost for
the
7.0 final. Any new code short before final increases the instability
risk of the final.Fe small functional regressions are probably not always critical. If a
(even small) functional regression breaks a lot, it is critical. If a
functional regression breaks a rare use case - it is not critical. If
improving a test helps to cover some critical code - so yes, it is
critical as well. Anything that is critical can involve anything
you've mentioned like adding tests or code.hmm, I understand your ideas, but I think maybe we should forbid such things as
well for this moment, because it make things complicated to judge(if it is critical
or not), such things can goes to 7.1let's only allows bugs fix (as you mentioned above) to be committed into
PHP-7.0 before the final release of its
Yeah, such things are quite stretchable, as we don't really assign tickets to milestones. Concentrating on hard weight things only were probably the best strategy, as you say. I'll be mentioning this yet in the next announcement as well.
Regards
anatol
Hello, internals!
Just noticed this thread and want to clarify some things with getClosure()
method. If i understood correctly, ReflectionFunctionAbstract::getClosure()
should return non-rebindable closure ONLY for internal functions, but for
userland functions binding should be still possible, right?
E.g.
class Foo {private $a=1;}
function test() {
var_dump($this);
}
$closure = (new \ReflectionFunction('test'))->getClosure();
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object));
$closure();
(unfortunately, it's broken: https://3v4l.org/YeDBj#v700rc5)
I know, that binding functions to objects is tricky, but this can be useful
for utility classes. But if you insist on this, ok, it's very rare case,
just one single example.
What is more important for me is ability to work with methods as closures.
So my main concern is about restrictions of ReflectionMethod->getClosure()
and rebinding.
See this example:
class Foo {private $a=1;}
class Bar {
function test() {
var_dump($this);
}
}
$closure = (new \ReflectionMethod('Bar','test'))->getClosure(new Bar);
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object));
$closure();
This kind of rebinding is heavily used by Go! Aspect-Oriented Framework and
now it will be broken (see https://3v4l.org/cZtbI#v700rc5). I think there
will be some more frameworks/libraries that use reflection and closure
rebinding via reflections. So this introduced patch for RC5 is definitely a
BC break for existing code.
Could you please provide a workaround or alternative ways to keep it
working? Maybe, restrict rebinding only for internal objects/methods will
be enough? Or only for ReflectionFunctionAbstract->getClosure(), but not
for ReflectionMethod->getClosure()
Thanks!
2015-10-13 10:27 GMT+03:00 Anatol Belski anatol.php@belski.net:
Hi,
-----Original Message-----
From: Xinchen Hui [mailto:laruence@php.net]
Sent: Tuesday, October 13, 2015 4:23 AM
To: Anatol Belski anatol.php@belski.net
Cc: Nikita Popov nikita.ppv@gmail.com; Dmitry Stogov <dmitry@zend.com
;
PHP internals internals@lists.php.net
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()Hey:
On Tue, Oct 13, 2015 at 6:12 AM, Anatol Belski anatol.php@belski.net
wrote:-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 10:33 PM
To: Anatol Belski anatol.php@belski.net
Cc: Dmitry Stogov dmitry@zend.com; PHP internals <
internals@lists.php.net>
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures
created by
ReflectionFunctionAbstract::getClosure()On Mon, Oct 12, 2015 at 10:22 PM, Anatol Belski
anatol.php@belski.net
wrote:Hi,
-----Original Message-----
From: Nikita Popov [mailto:nikita.ppv@gmail.com]
Sent: Monday, October 12, 2015 8:57 PM
To: Dmitry Stogov dmitry@zend.com
Cc: PHP internals internals@lists.php.net; Andrea Faulds
ajf@ajf.me;
Stas
Malyshev smalyshev@sugarcrm.com; Bob Weinand
bwoebi@php.net;
Anatol Belski anatol.php@belski.net
Subject: [PHP-DEV] Re: Forbid rebinding scope of closures
created by
ReflectionFunctionAbstract::getClosure()It would be great, if we stop any commits into PHP-7.0 except
for
critical fixes nowMaybe keep PHP-7.0 open (or as open as release branches usually
are),
but from
now on only cherry-pick critical fixes into PHP-7.0.0 (instead
of merging everything)?I commit myself to Dmitry's words. What matters today and
especially after
RC5 is the stability. Today we should invest into testing and bug
fixes more than into improvements (aka fixes to something that is
not broken). It really matters for the quality of the final.
That's the message to convey probably.To rephrase my question: Should we treat PHP-7.0 the same way we
treat
PHP-5.6 and other release branches (i.e. all kinds of bug fixes are
okay, even if
it's just improving a test or fixing some inconsequential behavior),
or
do you
want to limit the PHP-7.0 branch to actually critical fixes now?
From
what you
say, I assume the former rather than the latter?Talking about "critical" I meant usual definitions as something that
causes memory corruptions, data loss, big functional impact, security
flaws, etc.agree, bugs
The tricky point with 7.0 right now is that it's a lot of new stuff
with very high expectations standing short before final. Coupling this
with the "critical" from above, the definition I would make were - it
is a) critical and b) can be fixed properly and cannot wait until
after the final
release.
The dev time spent to fix something after 7.0 final is indeed lost for
the
7.0 final. Any new code short before final increases the instability
risk of the final.Fe small functional regressions are probably not always critical. If a
(even small) functional regression breaks a lot, it is critical. If a
functional regression breaks a rare use case - it is not critical. If
improving a test helps to cover some critical code - so yes, it is
critical as well. Anything that is critical can involve anything
you've mentioned like adding tests or code.hmm, I understand your ideas, but I think maybe we should forbid such
things as
well for this moment, because it make things complicated to judge(if it
is critical
or not), such things can goes to 7.1let's only allows bugs fix (as you mentioned above) to be committed into
PHP-7.0 before the final release of itsYeah, such things are quite stretchable, as we don't really assign tickets
to milestones. Concentrating on hard weight things only were probably the
best strategy, as you say. I'll be mentioning this yet in the next
announcement as well.Regards
anatol
Hi Alexander,
-----Original Message-----
From: Alexander Lisachenko [mailto:lisachenko.it@gmail.com]
Sent: Monday, October 19, 2015 10:19 AM
To: Anatol Belski anatol.php@belski.net
Cc: Xinchen Hui laruence@php.net; Nikita Popov nikita.ppv@gmail.com;
Dmitry Stogov dmitry@zend.com; PHP internals internals@lists.php.net
Subject: Re: [PHP-DEV] Re: Forbid rebinding scope of closures created by
ReflectionFunctionAbstract::getClosure()Hello, internals!
Just noticed this thread and want to clarify some things with getClosure()
method. If i understood correctly, ReflectionFunctionAbstract::getClosure()
should return non-rebindable closure ONLY for internal functions, but for
userland functions binding should be still possible, right?E.g.
class Foo {private $a=1;}
function test() {
var_dump($this);
}
$closure = (new \ReflectionFunction('test'))->getClosure();
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object)); $closure();(unfortunately, it's broken: https://3v4l.org/YeDBj#v700rc5)
I know, that binding functions to objects is tricky, but this can be useful for utility
classes. But if you insist on this, ok, it's very rare case, just one single example.What is more important for me is ability to work with methods as closures.
So my main concern is about restrictions of ReflectionMethod->getClosure() and
rebinding.See this example:
class Foo {private $a=1;}
class Bar {
function test() {
var_dump($this);
}
}
$closure = (new \ReflectionMethod('Bar','test'))->getClosure(new Bar); $object
= new Foo; $closure = $closure->bindTo($object, get_class($object)); $closure();This kind of rebinding is heavily used by Go! Aspect-Oriented Framework and
now it will be broken (see https://3v4l.org/cZtbI#v700rc5). I think there will be
some more frameworks/libraries that use reflection and closure rebinding via
reflections. So this introduced patch for RC5 is definitely a BC break for existing
code.
Could you please provide a workaround or alternative ways to keep it working?
Maybe, restrict rebinding only for internal objects/methods will be enough? Or
only for ReflectionFunctionAbstract->getClosure(), but not for
ReflectionMethod->getClosure()
This way it was re-enabled in RC5.
<?php
class Foo {private $a=1;}
$test = function() {
var_dump($this);
};
$closure = (new \ReflectionFunction($test))->getClosure();
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object)); $closure();
I'm not sure the first case you brought has no impact. If it doesn't, it should be probably fixed as well. But the second one was the exact goal of the change.
Regards
Anatol
On Mon, Oct 19, 2015 at 10:18 AM, Alexander Lisachenko <
lisachenko.it@gmail.com> wrote:
Hello, internals!
Just noticed this thread and want to clarify some things with getClosure()
method. If i understood correctly, ReflectionFunctionAbstract::getClosure()
should return non-rebindable closure ONLY for internal functions, but for
userland functions binding should be still possible, right?E.g.
class Foo {private $a=1;}
function test() {
var_dump($this);
}
$closure = (new \ReflectionFunction('test'))->getClosure();
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object));
$closure();(unfortunately, it's broken: https://3v4l.org/YeDBj#v700rc5)
I know, that binding functions to objects is tricky, but this can be
useful for utility classes. But if you insist on this, ok, it's very rare
case, just one single example.What is more important for me is ability to work with methods as closures.
So my main concern is about restrictions of ReflectionMethod->getClosure()
and rebinding.See this example:
class Foo {private $a=1;}
class Bar {
function test() {
var_dump($this);
}
}
$closure = (new \ReflectionMethod('Bar','test'))->getClosure(new Bar);
$object = new Foo;
$closure = $closure->bindTo($object, get_class($object));
$closure();This kind of rebinding is heavily used by Go! Aspect-Oriented Framework
and now it will be broken (see https://3v4l.org/cZtbI#v700rc5). I think
there will be some more frameworks/libraries that use reflection and
closure rebinding via reflections. So this introduced patch for RC5 is
definitely a BC break for existing code.
Could you please provide a workaround or alternative ways to keep it
working? Maybe, restrict rebinding only for internal objects/methods will
be enough? Or only for ReflectionFunctionAbstract->getClosure(), but not
for ReflectionMethod->getClosure()
This change is primarily targeting userland methods, so your use-case is
exactly the one this is supposed to prevent. Note that you can still use
->bindTo($object). The only thing you cannot do is ->bindTo($object,
get_class($object)).
Nikita
2015-10-19 12:19 GMT+03:00 Nikita Popov nikita.ppv@gmail.com:
This change is primarily targeting userland methods, so your use-case is
exactly the one this is supposed to prevent. Note that you can still use
->bindTo($object). The only thing you cannot do is ->bindTo($object,
get_class($object)).
It's very pity ( It was a very interesting feature that gives a birth for
userland AOP, without it part of framework functionality will be lost.
From the list of attached bugs I can see that all weird stuff happens only
with internal classes/functions/methods. I never have seen a segfaults in
my code that heavily uses closure rebinding with userland methods/classes
and expect it to work in all future versions of PHP, starting from >=5.4.
I don't know PHP engine well, but this feature with scope rebinding for
methods allows some interesting patterns, like "Spy", "Field initializer",
"Lazy proxy initializer", etc. We just take one method and bind it to
another scope to perform an additional logic if needed.
Maybe, apply your patch only to internal functions (restrict them to have
$this inside) and simple functions? This will cover most bugs, but won't
break an exisitng code.
On Mon, Oct 19, 2015 at 12:34 PM, Alexander Lisachenko
lisachenko.it@gmail.com wrote:
2015-10-19 12:19 GMT+03:00 Nikita Popov nikita.ppv@gmail.com:
This change is primarily targeting userland methods, so your use-case is
exactly the one this is supposed to prevent. Note that you can still use
->bindTo($object). The only thing you cannot do is ->bindTo($object,
get_class($object)).It's very pity ( It was a very interesting feature that gives a birth for
userland AOP, without it part of framework functionality will be lost.
From the list of attached bugs I can see that all weird stuff happens only
with internal classes/functions/methods. I never have seen a segfaults in
my code that heavily uses closure rebinding with userland methods/classes
and expect it to work in all future versions of PHP, starting from >=5.4.I don't know PHP engine well, but this feature with scope rebinding for
methods allows some interesting patterns, like "Spy", "Field initializer",
"Lazy proxy initializer", etc. We just take one method and bind it to
another scope to perform an additional logic if needed.Maybe, apply your patch only to internal functions (restrict them to have
$this inside) and simple functions? This will cover most bugs, but won't
break an exisitng code.
This is very tricky use case IMO.
We should absolutely focus on stability, and if we can't find a way to
make things safe, we forbid them
for the stability sake. If there are no way to have stable things,
well, that's pitty but it's like that.
We can't allow PHP to release with a known crashing behavior.
Perhaps we'll find new ways to do things in 7.1 or next 8.0.
Julien.Pauli
2015-10-22 17:48 GMT+02:00 Julien Pauli jpauli@php.net:
This is very tricky use case IMO.
We should absolutely focus on stability, and if we can't find a way to
make things safe, we forbid them
for the stability sake. If there are no way to have stable things,
well, that's pitty but it's like that.We can't allow PHP to release with a known crashing behavior.
I can only second this, we had a similar issue with closures and $this
in 5.3 which wasn't properly resolved until a later version, I think
it was 5.4. We're at RC5 now and stability is an absolute must for us
to have a proper release of our next major.
Rather leave behaviour we don't have a proper solution for disabled
for now until we can come in with a proper patch to avoid additional
hacks in the code that will become even trickier to handle once the
issue is resolved.
--
regards,
Kalle Sommer Nielsen
kalle@php.net
2015-10-22 18:48 GMT+03:00 Julien Pauli jpauli@php.net:
This is very tricky use case IMO.
We should absolutely focus on stability, and if we can't find a way to
make things safe, we forbid them
for the stability sake. If there are no way to have stable things,
well, that's pitty but it's like that.We can't allow PHP to release with a known crashing behavior.
Perhaps we'll find new ways to do things in 7.1 or next 8.0.
Good morning! Thank you, Julien, for paying an attention to my message and
pointing to the things that are more important. I'm absolutely agree with
you that the stability of the core is priority task for now, so it will be
better to keep php7.0 core as stable as possible.
Of course, this patch introduces some BC breaks, because now code that
accepts \Closure instance should be able to check, if this instance of
closure from ReflectionFunction or not to decide about possibility of
binding. Technically they should be equal.
Today I was able to test my code with RC5 and things aren't so bad, all
main features working, except minor ones ) All dynamic methods can be
called with closures, because I only need instance rebinding for closure,
not the scope, because it's already passed as argument for
getClosure($this). Simplified example https://3v4l.org/WRuZ6. For static
methods, keeping LSB with closures is tricky, but it works via
forward_static_call_array and putting it into the closure, which can be
easily binded to the required scope: https://3v4l.org/kLN0E
So, it's not a big deal for me anymore, all other things are tricky and
aren't used very often. Thanks!