Hey Rob, > On 11. Jul 2025, at 01:43, Rob Landers <rob@bottled.codes> wrote: >> >> Nick previously suggested having the get-hook's first return value cached; >> it would still be subsequently called, so any side effects would still >> happen (though I don't know why you'd want side effects), but only the first >> returned value would ever get returned. Would anyone find that acceptable? >> (In the typical case, it would be the same as the current $this->foo ??= >> compute() pattern, just with an extra cache entry.) >> >> --Larry Garfield >> > > I think that only covers one use-case for getters on readonly classes. Take > this example for discussion: > > readonly class User { > public int $elapsedTimeSinceCreation { get => time() - $this->createdAt; } > private int $cachedResult; > public int $totalBalance { get => $this->cachedResult ??= 5+10; } > public int $accessLevel { get => getCurrentAccessLevel(); } > public function __construct(public int $createdAt) {} > } > > $user = new User(time() - 5); > var_dump($user->elapsedTimeSinceCreation); // 5 > var_dump($user->totalBalance); // 15 > var_dump($user->accessLevel); // 42 > > In this example, we have three of the most common ones: > Computed Properties (elapsedTimeSinceCreation): these are properties of the > object that are relevant to the object in question, but are not static. In > this case, you are not writing to the object. It is still "readonly". > Memoization (expensiveCalculation): only calculate the property once and only > once. This is a performance optimization. It is still "readonly". > External State (accessLevel): properties of the object that rely on some > external state, which due to architecture or other convienence may not make > sense as part of object construction. It is still "readonly". > You can mix-and-match these to provide your own level of immutability, but > memoization is certainly not the only one. > > You could make the argument that these should be functions, but I'd posit > that these are properties of the user object. In other words, a function to > get these values would probably be named `getElapsedTimeSinceCreation()`, > `getTotalBalance`, or `getAccessLevel` -- we'd be writing getters anyway. > > — Rob
Please remember that the RFC will allow `readonly` on backed properties only, not on virtual hooked properties. Nothing from your example would work, and it would result in: Fatal error: Hooked virtual properties cannot be declared readonly My proposed alternative implementation with caching addresses the concern Claude and Tim had and will make this hold: ```php class Unusual { public function __construct( public readonly int $value { get => $this->value * random_int(1, 100); } ) {} } $unusual = new Unusual(1); var_dump($unusual->value === $unusual->value); // true ``` – Nick