Hi internals!
Currently, inside instance methods the following invariant holds:
assert(is_null($this) || is_object($this))
This is a problem. I'd like to guarantee the following invariant instead:
assert(is_null($this) || $this instanceof self)
That is, ensure that if $this is not null, then it must be an object
"compatible" with the class, i.e. be in its inheritance hierarchy.
The absence of this guarantee, apart from being a major WTF in itself, also
prevents us from optimizing operations involving $this. In particular, the
following optimizations are not possible:
a) If typed properties land, we will not be able to use the type of
$this->typedProperty for optimization, because $this->typedProperty might
actually be referencing a property of a totally unrelated class.
b) We are not able to early-bind arguments to private or final method
calls, i.e. determine at compile-time whether an argument will use by-value
or by-reference argument passing and optimize accordingly.
c) We are not able to inline calls to private or final methods. (The
possibility of null is an issue in this instance as well, though a lesser
one.)
The good news is that, as of PHP 7.0, we are very close to guaranteeing
that "$this instanceof self" already. There exists only one little known
loophole: Obtaining a closure object wrapping the method using
ReflectionMethod::getClosure() and binding to an unrelated $this using
Closure::bindTo().
In PHP 7.0 we already heavily cut down 1 on what bindTo() is to allowed
to do on fake method closures. Namely, it was completely forbidden to
rebind the scope of methods, as this was already interacting badly with
optimizations in 7.0. We did not forbid binding to an incompatible $this
because it was not yet causing immediate issues at the time.
I'd like to remedy this oversight now and restrict $this binding of fake
method closures to compatible contexts only, i.e. for a method A::foo()
allow only instances of A or one of its descendant classes to be bound.
This limitation already exists for fake method closures targeting internal
methods.
Are there any objections to this change? If not, I'll merge the patch 2.
If yes, I'll write an RFC, as this change, while of no direct consequence
for PHP programmers, is important for the PHP implementation.
Regards,
Nikita
Hi internals!
Currently, inside instance methods the following invariant holds:
assert(is_null($this) || is_object($this))
This is a problem. I'd like to guarantee the following invariant instead:
assert(is_null($this) || $this instanceof self)
That is, ensure that if $this is not null, then it must be an object
"compatible" with the class, i.e. be in its inheritance hierarchy.The absence of this guarantee, apart from being a major WTF in itself, also
prevents us from optimizing operations involving $this. In particular, the
following optimizations are not possible:a) If typed properties land, we will not be able to use the type of
$this->typedProperty for optimization, because $this->typedProperty might
actually be referencing a property of a totally unrelated class.
b) We are not able to early-bind arguments to private or final method
calls, i.e. determine at compile-time whether an argument will use by-value
or by-reference argument passing and optimize accordingly.
c) We are not able to inline calls to private or final methods. (The
possibility of null is an issue in this instance as well, though a lesser
one.)The good news is that, as of PHP 7.0, we are very close to guaranteeing
that "$this instanceof self" already. There exists only one little known
loophole: Obtaining a closure object wrapping the method using
ReflectionMethod::getClosure() and binding to an unrelated $this using
Closure::bindTo().In PHP 7.0 we already heavily cut down 1 on what bindTo() is to allowed
to do on fake method closures. Namely, it was completely forbidden to
rebind the scope of methods, as this was already interacting badly with
optimizations in 7.0. We did not forbid binding to an incompatible $this
because it was not yet causing immediate issues at the time.I'd like to remedy this oversight now and restrict $this binding of fake
method closures to compatible contexts only, i.e. for a method A::foo()
allow only instances of A or one of its descendant classes to be bound.
This limitation already exists for fake method closures targeting internal
methods.Are there any objections to this change? If not, I'll merge the patch 2.
If yes, I'll write an RFC, as this change, while of no direct consequence
for PHP programmers, is important for the PHP implementation.Regards,
Nikita
Hi,
Will this patch break any use cases similar to:
class Bar {
private $a = 'hidden';
}
$x = new Bar();
$y = function() { return $this->a);
$z = $x->bindTo($x, $x);
echo $z(); //hidden
?
If not, I have no objections.
Hi internals!
Currently, inside instance methods the following invariant holds:
assert(is_null($this) || is_object($this))
This is a problem. I'd like to guarantee the following invariant instead:
assert(is_null($this) || $this instanceof self)
That is, ensure that if $this is not null, then it must be an object
"compatible" with the class, i.e. be in its inheritance hierarchy.The absence of this guarantee, apart from being a major WTF in itself,
also
prevents us from optimizing operations involving $this. In particular, the
following optimizations are not possible:a) If typed properties land, we will not be able to use the type of
$this->typedProperty for optimization, because $this->typedProperty might
actually be referencing a property of a totally unrelated class.
b) We are not able to early-bind arguments to private or final method
calls, i.e. determine at compile-time whether an argument will use
by-value
or by-reference argument passing and optimize accordingly.
c) We are not able to inline calls to private or final methods. (The
possibility of null is an issue in this instance as well, though a lesser
one.)The good news is that, as of PHP 7.0, we are very close to guaranteeing
that "$this instanceof self" already. There exists only one little known
loophole: Obtaining a closure object wrapping the method using
ReflectionMethod::getClosure() and binding to an unrelated $this using
Closure::bindTo().In PHP 7.0 we already heavily cut down 1 on what bindTo() is to allowed
to do on fake method closures. Namely, it was completely forbidden to
rebind the scope of methods, as this was already interacting badly with
optimizations in 7.0. We did not forbid binding to an incompatible $this
because it was not yet causing immediate issues at the time.I'd like to remedy this oversight now and restrict $this binding of fake
method closures to compatible contexts only, i.e. for a method A::foo()
allow only instances of A or one of its descendant classes to be bound.
This limitation already exists for fake method closures targeting internal
methods.Are there any objections to this change? If not, I'll merge the patch 2.
If yes, I'll write an RFC, as this change, while of no direct consequence
for PHP programmers, is important for the PHP implementation.Regards,
NikitaHi,
Will this patch break any use cases similar to:
class Bar {
private $a = 'hidden';
}$x = new Bar();
$y = function() { return $this->a);
$z = $x->bindTo($x, $x);
echo $z(); //hidden?
If not, I have no objections.
Nope. Rebinding on "real" closures continues to work as usual, including
using it to access private properties of objects.
This change only affects closures created by
ReflectionMethod::getClosure(), i.e. closures that are really methods in
disguise.
Nikita
I agree, this ability is a dirty and annoying hack, but I'm sure, some people use it.
Tony, you don't use this in the new runkit replacement? :)
Thanks. Dmitry.
I agree, this ability is a dirty and annoying hack, but I'm sure, some
people use it.Tony, you don't use this in the new runkit replacement? :)
Thanks. Dmitry.
Is this referring to https://github.com/badoo/soft-mocks? If so, searching
the codebase for getClosure gives no results, so it shouldn't be affected.
I know that the Go! AOP framework used to do some odd things with closure
binding on methods. However their use case was already completely broken
by the limitations we added in 7.0 and the feature was removed.
Nikita
Hello, internals!
Go! AOP was used closure rebinding to an incompatible context only for one
minor specific feature, called privileged advices, where method was
executing in the context of target class. But all main closure binding in
the framework core work only with compatible contexts, so everything is ok
with PHP7 now.
So, this new patch is ok for me. Anyway, if I need this functionality
again, I can convert methods to the closures via AST transformation of
classes.
2016-03-30 14:02 GMT+03:00 Nikita Popov nikita.ppv@gmail.com:
I agree, this ability is a dirty and annoying hack, but I'm sure, some
people use it.Tony, you don't use this in the new runkit replacement? :)
Thanks. Dmitry.
Is this referring to https://github.com/badoo/soft-mocks? If so, searching
the codebase for getClosure gives no results, so it shouldn't be affected.I know that the Go! AOP framework used to do some odd things with closure
binding on methods. However their use case was already completely broken
by the limitations we added in 7.0 and the feature was removed.Nikita
Hi internals!
Currently, inside instance methods the following invariant holds:
assert(is_null($this) || is_object($this))
This is a problem. I'd like to guarantee the following invariant instead:
assert(is_null($this) || $this instanceof self)
That is, ensure that if $this is not null, then it must be an object
"compatible" with the class, i.e. be in its inheritance hierarchy.The absence of this guarantee, apart from being a major WTF in itself,
also prevents us from optimizing operations involving $this. In particular,
the following optimizations are not possible:a) If typed properties land, we will not be able to use the type of
$this->typedProperty for optimization, because $this->typedProperty might
actually be referencing a property of a totally unrelated class.
b) We are not able to early-bind arguments to private or final method
calls, i.e. determine at compile-time whether an argument will use by-value
or by-reference argument passing and optimize accordingly.
c) We are not able to inline calls to private or final methods. (The
possibility of null is an issue in this instance as well, though a lesser
one.)The good news is that, as of PHP 7.0, we are very close to guaranteeing
that "$this instanceof self" already. There exists only one little known
loophole: Obtaining a closure object wrapping the method using
ReflectionMethod::getClosure() and binding to an unrelated $this using
Closure::bindTo().In PHP 7.0 we already heavily cut down 1 on what bindTo() is to allowed
to do on fake method closures. Namely, it was completely forbidden to
rebind the scope of methods, as this was already interacting badly with
optimizations in 7.0. We did not forbid binding to an incompatible $this
because it was not yet causing immediate issues at the time.I'd like to remedy this oversight now and restrict $this binding of fake
method closures to compatible contexts only, i.e. for a method A::foo()
allow only instances of A or one of its descendant classes to be bound.
This limitation already exists for fake method closures targeting internal
methods.Are there any objections to this change? If not, I'll merge the patch 2.
If yes, I'll write an RFC, as this change, while of no direct consequence
for PHP programmers, is important for the PHP implementation.Regards,
Nikita
As the discussion was positive, this is now merged as
https://github.com/php/php-src/commit/75af8150f58fb55637ac12b33d469b27adef9d76
.
Nikita
Hi Nikita,
As the discussion was positive, this is now merged as
https://github.com/php/php-src/commit/75af8150f58fb55637ac12b33d469b27adef9d76
Could you write UPGRADING for this?
Regards,
--
Yasuo Ohgaki
yohgaki@ohgaki.net