First, my apologies to the readers. Something is chewing up my formatting and indenting. I’m still trying to figure out a cause.
If we ignore implementation details and ownership it's pretty similar to what we have with traits. In PHP, what would be the benefit of extensions over traits? One thing that comes to my mind is that traits can be used by whatever chooses to add it (back to ownership). Good one, but what else? If ownership is the only concern then maybe a small addition to traits could achieve the same?
trait Foo
{
for Baz\Bar::class; // inverse of "use" to limit usage
}
class Bar
{
use Foo;
}
Brings conflict resolution for free; and I think it would be cheaper than yet another class-like thing. Maybe also feels more php-ish since we already have the trait concept?
If we ignore precise syntax and implementation, this is exactly the mental model of my RFCs and implementations. It is applying a trait to an existing class that you don’t control.
The similarities don’t end there. This is the same infrastructure as what is used to implement this feature.
The reason I don’t feel the trait … for syntax is appropriate is because of $this. In a function inside a typical trait, $this refers to an instance of the trait. In an extension, $this refers to an instance of the class being extended. Adding the for block to the trait changes the type of $this in the entire trait. That is unintuitive and likely to introduce barriers to understanding.
Holly
The reason I don’t feel the
trait … forsyntax is appropriate is because of$this. In a function inside a typical trait,$thisrefers to an instance of the trait. In an extension,$thisrefers to an instance of the class being extended.
I don't think that's true - there's no such thing as an "instance of a trait", the contents of a trait are pasted directly into the class where they're used, and act just as if they were written inline at that point.
What that does mean is that they have access to private members, which I understand extensions would not, so that might be a good reason to use a new keyword.
Rowan Tommins
[IMSoP]
The reason I don’t feel the
trait … forsyntax is appropriate is because of$this. In a function inside a typical trait,$thisrefers to an instance of the trait. In an extension,$thisrefers to an instance of the class being extended.I don't think that's true - there's no such thing as an "instance of a trait", the contents of a trait are pasted directly into the class where they're used, and act just as if they were written inline at that point.
What that does mean is that they have access to private members, which I understand extensions would not, so that might be a good reason to use a new keyword.
Rowan Tommins
[IMSoP]
I was unclear in my description. $this in a trait will be an instance of what it applies to and will pass runtime checks via instanceof. What I was referring to is that at compile-time from inside a function of the trait, it can only call members known to the trait. Whereas in an extension, the entire public surface of the extended type is known and available, plus the members of the extension.
So that isn’t to say trait … for couldn’t work, but it feels very different to a trait in its use.
What I was referring to is that at compile-time from inside a function of the trait, it can only call members known to the trait.
There is no such check in PHP. Static Analysis tools may warn about it, but a trait will compile fine with code that refers to unknown members. The code will then succeed or fail at runtime just like any other object access: https://3v4l.org/QIPsL
Regards,
Rowan Tommins
[IMSoP]
There is no such check in PHP. Static Analysis tools may warn about it, but a trait will compile fine with code that refers to unknown members. The code will then succeed or fail at runtime just like any other object access:
I have to admit I never knew that worked. I genuinely learned something new today.
Now knowing that, it makes the case for using a different keyword like extension much stronger since that is not the case for an extension. Everything on an extension is type-checked at compile time, save some edge cases with scalars.
Now knowing that, it makes the case for using a different keyword like
extensionmuch stronger since that is not the case for an extension. Everything on an extension is type-checked at compile time, save some edge cases with scalars.
I'm not sure if I'm misunderstanding, but if you're saying that every reference to $this in the body of an extension method will be analysed by the compiler against the class definition, that would be extremely surprising to PHP users.
As far as I know, all method and property despatch in PHP is dynamic, whether it's on $this or any other object reference. In other words, only when you actually run code like $this->whatever() does PHP even try to look for the method definition.
Trying to validate that at compile time would presumably break any dynamic code - $this->whatever() would not be allowed to fall back to __call('whatever'), and $this->{$someVar}() would have to refuse to compile.
While we could argue whether those features make PHP better or worse, having them forbidden just in the context of extension methods would feel very arbitrary.
However, perhaps we're talking at cross purposes, and this is not actually what you're proposing.
Regards,
Rowan Tommins
[IMSoP]
I'm not sure if I'm misunderstanding, but if you're saying that every reference to $this in the body of an extension method will be analysed by the compiler against the class definition, that would be extremely surprising to PHP users.
As far as I know, all method and property despatch in PHP is dynamic, whether it's on $this or any other object reference. In other words, only when you actually run code like $this->whatever() does PHP even try to look for the method definition.
I’m trying to speak vaguely to keep messages brief because I’m generally typing from my phone. In doing so, I’m adding inaccurate edge cases which you’re astutely noticing, but they aren’t the details I was focused on. For full details, please read the draft RFC I posted as a Gist. https://gist.github.com/hollyschilling/f590f3a1e488732eea5b0d8014702276
Dispatched inside an extension method is exactly the same as anywhere else besides that $this only has the public surface accessible. If a user calls a method name that doesn’t exist, it does the same as usual. That hasn’t changed.
Dispatched inside an extension method is exactly the same as anywhere else besides that
$thisonly has the public surface accessible. If a user calls a method name that doesn’t exist, it does the same as usual. That hasn’t changed.
Gotcha. The confusion came from the mention of "compile-time", which isn't when visibility checks happen. The method is looked up, then its visibility flags are enforced, all at runtime.
So another way of putting it is that an extension method has no privileged access to the target class.
That is indeed different from a trait. Whereas a trait is pasted into the class, an extension function stays separate and $this is just a normal object reference being accessed "publicly".
Regards,
Rowan Tommins
[IMSoP]