> Le 8 juin 2025 à 06:16, Larry Garfield <la...@garfieldtech.com> a écrit : > > As Nick has graciously provided an implementation, we would like to open > discussion on this very small RFC to allow `readonly` on backed properties > even if they have a hook defined. > > https://wiki.php.net/rfc/readonly_hooks > > -- > Larry Garfield > la...@garfieldtech.com
Hi Larry, Nick, Last summer, the question of allowing hooks on readonly has been raised as part of the RFC «Property hooks improvements», and at that time I have raised an objection on allowing the get hook on readonly properties and I have suggested for a better design for the main issue it was supposed to solve, see https://externals.io/message/124149#124187 and the following messages. (The RFC itself was trimmed down to the non-controversial part.) I’ll repeat here both my objection and my proposal for better design, but more strongly, with the hope that the message will be received. The purpose of readonly properties is (citing the original RFC, https://wiki.php.net/rfc/readonly_properties_v2#rationale) to provide strong immutable guarantee, i.e.: ```php class Test { public readonly string $prop; public function method(Closure $fn) { $prop = $this->prop; $fn(); // Any code may run here. $prop2 = $this->prop; assert($prop === $prop2); // Always holds. } } ``` By allowing a get hook on readonly property, you are effectively nullifying this invariant. Invariants must be enforced be the engines (whenever possible; there is an inevitable loophole until the property is initialised), and not left to the discretion of the user. If a get hook on readonly property is allowed, a random user will use its creativity in order to circumvent the intended invariant (recall: immutability). I say “creativity”, not “dumbness”, because you cannot mechanically tell the two apart: ```php class doc { public readonly int page { get => $this->page + $this->offset; } private int $offset = 0; public function __construct(int $page) { $this->page = $page; } public function foo() { // $this->offset may be adjusted here } } ``` I know that some people won’t see a problem with that code (see the cited thread above), and this is a strong reason not to allow that: you cannot trust the user to enforce invariants that they don’t understand or are not interested in. (The objection above is for the `get` hook`; there is no such issue with the `set` hook.) Now, here is the suggestion for a better alternative design, that (1) don’t allow to break the invariant of immutability, (2) solve the issue of lazy initialisation (which is, I guess, the main purpose of the `get` hook on readonly), and (3) also works with nullable properties: Add an additional hook to backed properties, named `init`. When attempting to read the value of the backing store, if it is uninitialised, then the init hook is triggered, which is supposed to initialise it. —Claude