-
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
Feature request: Allow unsetting of backed property hooks #17922
Comments
cc @iluuu1994 |
related with #9389, there should be our usecase is https://github.com/atk4/core/blob/5.2.0/src/Phpunit/TestCase.php#L115 - we want to reuse some objects, ie. reset them to initial state, currently, it is not possible |
Also note that
I think that would be reasonable, although the magic method question above remains. |
I don't have any insight into PHP internals, but as a developer if I Another issue in favor of <?php
class ForumsPost{
public string $Contents;
public int $WordCount{
get{
if(!isset($this->WordCount)){
$this->WordCount = /* Some complex, expensive logic working on `ForumsPost::$Contents`... */;
}
return $this->WordCount;
}
}
}
$fp = new ForumsPost();
$fp->Contents = 'Foo';
print($fp->WordCount); // Prints `1`.
$fp->Contents = 'Foo Bar';
print($fp->WordCount); // Wrong, still prints `1`. We have to unset `ForumsPost::$WordCount` to recalculate it...
unset($fp->WordCount); // Error - not allowed in PHP 8.4!
print($fp->WordCount); // Would print `2` if `unset()` were allowed. In this example, the logic to calculate and cache the word count is in the getter for The workaround is to put the word count logic in a separate public method accessible to both the getter and other callers, just like we would have done if property hooks didn't exist at all. But doesn't that kind of defeat the point of property hooks? Now we've added extra methods merely to work around a language limitation, and the caller must be aware enough to determine when a recalculation is desirable and remember to call those methods in those cases. We're in a position not much different than if property hooks didn't exist at all. IMHO much of the benefit of property hooks is being able to connect logic to the getting of and changing of properties, so that the gory details of their calculation are hidden from the caller. If we have to resort to exposing extra public methods because we can't |
This is the default behavior for properties. https://3v4l.org/W3SAZ Since backing properties are really just properties and follow the same rules, this would also apply there. But as you say, most people would not expect that. It's mostly problematic for We also need to differentiate between
You can do: class C {
private $_prop;
public $prop {
get => $this->_prop ??= <initialize>;
}
public function invalidateCache() {
unset($this->_prop);
}
} Yes, this requires a separate method for cache invalidation (not for accessing), but it's certainly a cleaner API than undocumented |
Not sure why that would be. From my perspective as a user of PHP and not a maintaner, if I
Indeed. But in my view if we're forced to add methods to operate on property hook internals - which ideally should be totally hidden from the caller - we may as well not use property hooks at all because we're no better off than the pre-8.4 state of things. In fact we might be worse off, because now we're mixing new-style property hooks with public methods that operate on logic hidden in property hooks, which I think is much more confusing. The caller now requires intimate knowledge of object internals which by all rights should be hidden, and the attention to detail to use those methods consistently every time. That will result in bugs and confusing code. In fact I would argue that in the case of cacheable property hooks, it would be clearer to simply not use property hooks in their current state at all because of this issue. But that's not what I want - I like property hooks! (I was a C# developer decades ago where I grew to like what they called "accessors".)
I don't think cost to refactor should be a major consideration. Refactoring will always be hard, but the cost of an incomplete implementation of property hooks is much higher - it can result in them not being used at all, like we've decided to do! |
I don't think we'll reach consensus here. I would encourage you to start a discussion on the mailing list (GH is used for bugs or simple feature requests) if you feel this should be pursued. In any case, it will require an RFC. I actually think
To be clear, it was never our intention to cover 100% of the use-cases. Lazy properties that require cache invalidation go into the niche category in my book, and while you can still solve this with property hooks, you'll need a separate cache property.
It's about breaking APIs, rather than refactoring. |
Perhaps, but it's what we have and it's not going anywhere. I think
I would push back on this a little - lazy loading and caching of objects is very common in ORMs and is very useful when converting DB rows into complex objects that don't need every single sub-object retrieved from the DB at once. I would not consider that a niche use case. In fact I would call object retrieval and caching one of the primary use cases of property hooks. IMHO PHP isn't going to be making any drastic changes like removing From my end, we will not be using property hooks at all because of this issue, which is a great pity because I love the idea and want to use it! |
As mentioned, IMO at least, the main issue is that properties should not be uninitialized at all. If some properties remain uninitialized after constructions, there's no way they'll know through the API without reading your code. If "uninitialized" is a possibility that they need to handle, their properties should be nullable, or some other union type.
Lazy loading doesn't pose a problem though. It's only cache invalidation from outside the property. But IMO this leaks implementation details.
I don't quite follow this reasoning. C# doesn't offset Anyway, I think I'll leave it here with the discussion, since it doesn't look like we'll be on the same page regarding this issue. 🙂 That's why I suggested consulting the mailing list. Maybe other people have other view points. |
We can't use Our reasoning for rejecting property hooks is because if we were to switch our codebase to property hooks, it should be an all-in update, not one that uses hooks for some things but not for others because of this (or any other) limitation that leaves the language inconsistent with itself. I created this issue because this problem actually happened in our real-world codebase as we were exploring property hooks. A codebase using a feature some times but not others will be inconsistent and much more confusing to read. Consistency is more important than cool features. |
Description
(I hope this is the right place to suggest this, if not I'm happy to move it elsewhere.)
In the new property hooks feature of PHP 8.4, I think it would be very useful to be able to
unset()
backed properties.Consider the following object, a forums post with a user object representing the poster which is lazy-loaded with a getter:
Now suppose we have already retrieved the
User
property, and later we change theUserId
. Now theUserId
property and theUser
property are now out of sync. What if we could add a setter hook for theUserId
property, tounset()
any cachedUser
object at the moment when we set theUserId
? For example:The current state of affairs means that if we want to return a backed property to a "not initialized" state, we have to use kludges like making it a nullable type and calling
null
"not initialized" by convention, or setting it to say an empty string and calling that "not initialized" by convention. Both of these are poor, semantically wrong solutions, especially considering it's already allowed tounset()
plain object properties directly.Additionally, the consumer of an object might find the following behavior surprising:
The consumer of an object should not need to know the internal implementation of properties. In this example it is well-established in PHP that we can
unset()
object properties, but since plain properties and property hooks are indistinguishable to the consumer,unset()
on a property can result in an error if the internal implementation is a property hook. This is surprising to the consumer, who doesn't and shouldn't care how the property is implemented internally.In the RFC for property hooks,
unset()
is explicitly disallowed because "it would involve bypassing any set hook that is defined". But I'm not sure whyunset()
ing something would invoke the setter.The text was updated successfully, but these errors were encountered: