-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Property hooks do not call parent's magic __get
and __set
methods when referencing parent property
#17542
Comments
|
I agree. It's honestly not something that we explicitly considered, but I agree that |
Thanks for the quick response! The manual currently says (emphasis mine):
It also gives an example of referencing the parent's property (not hook): <?php
class Strings
{
public string $val;
}
class CaseFoldingStrings extends Strings
{
public bool $uppercase = true;
public string $val {
get => $this->uppercase
? strtoupper(parent::$val::get())
: strtolower(parent::$val::get());
}
}
?> So I don't see how this is parent-hook-specific, since you can access plain properties. And if you can access a plain property, you should be able to access a private or non-existent one via That said, if the workaround approach is preferred, I think at the least the error messaging could be changed. My mental model is that you won't see a |
Right. This sentence implies that the parent declares such a property, but without a hook for the given operation, in which case we'll resort to accessing the properties backing store. The idea of this fallback is to allow adding hooks to child properties in a backwards compatible way, i.e. by avoiding direct access of the backing value that would break if the parent were to add a hook itself (or more precisely, it would circumvent the parents hook). I don't think your expectation for this to work is unreasonable, but it wasn't documented, and thus is technically a language change. I personally don't think we need this fallback, and it might not be quite straight forward to implement. But if you feel strongly about this topic, feel free to ask for opinions on the internals mailing list.
Can you make a concrete suggestion? |
I can see where you're coming from, but to me it just implies "it will try to fetch the property normally", where normally means "it will follow the existing property access patterns", and hence why I'd expect
I don't think I feel too strongly, but yeah I might raise it there; I think it'd be nice to either be consistent, or more strongly indicate that this will only work for existing properties or hooks. On that note...
This is tough, because in a sense there is an "
..." I think I'd be satisfied with this, because we're calling out that this language construct ( |
Mm, yeah, we never really thought about this case. I would not be opposed to falling back to |
Just to clarify:
This section explicitly refers to a declared parent property. So, it's not really a matter of interpretation. However, the case where the parent property is not declared is unspecified. It should have been explicitly declared whether this will error, or calling magic methods. That said, I believe calling magic methods would also not be consistent, as accessing properties from the parent class will actually use the child property if declared there, rather than calling magic methods. E.g. https://3v4l.org/06HfX class P {
public function setProp() {
$this->prop = 'foo';
}
public function __set($name, $value) {
var_dump('__set', $name, $value);
}
}
class C extends P {
public $prop;
}
$c = new C();
$c->setProp();
var_dump($c); Furthermore, if the parent property does not contain magic get/set, does that mean we're resorting to dynamic properties? That's would inevitably clash with non-virtual child properties. Similarly, what happens with It's not obvious to me what the correct, consistent behavior is here. |
I appreciate that the intent was to refer to declared parent properties, and I agree that the intent is not up for interpretation either! However, when I read that section, it didn't say declared, just "parent property", and mistakenly or not I walked away from that paragraph thinking that I could do something equivalent to Mainly I want to stress that I think that the nomenclature we use isn't necessarily straightforward, so maybe there's room for improvement here. I don't think I'm off the mark with this observation since property hooks themselves introduce virtual properties, adding to the mix. That said, I also want to say that I appreciate the time and effort that went into property hooks and their documentation, and this is not meant as criticism.
I actually think your example is consistent, at least with my mental model. I would say that is different than the code I gave in my example, because
We could, and maybe that clash would be okay?
I didn't quite follow this one - what happens with That said, I appreciate all the edge cases here, so:
I'm fine with continuing to require it to be a declared / defined property, especially since that could always be relaxed in the future. Did you have an opinion about my suggestion for an error message like |
Oops, this should have said "dynamic properties". |
Sorry for the delay.
Correct. Technically, magic methods don't involve properties, rather they are a fallback for properties. Anyway, terminology aside:
This is problematic because the engine cannot differentiate between the declared and dynamic property anymore. We would need more complex mechanisms to disambiguate, and I don't think this is a good area to invest energy into.
Isn't this essentially just a more properly worded sentence for "Undefined property ParentClass::$foo"? To really explain, we would have to add something like "rather than dynamic or magic method property", but that's quite a mouthful. Error message hints would be nice, like compilers have. 🙂 |
Description
I was playing around with property hooks and noticed the following (in my opinion) inconsistency when combining property hooks with inheritance and
__get
and__set
:The following code (for
__get
, the same applies for__set
in https://3v4l.org/Q2OAj):Resulted in this output:
But I expected this output instead:
I tried searching the RFC and existing PHP issues, but I haven't found this example - apologies if I missed it. Barring something saying that this shouldn't work, I would expect it to.
PHP Version
PHP 8.4.3
Operating System
No response
The text was updated successfully, but these errors were encountered: