Re: [RFC] [DISCUSSION] Immutable/final/readonly properties

From: Date: Tue, 25 Feb 2020 11:24:51 +0000
Subject: Re: [RFC] [DISCUSSION] Immutable/final/readonly properties
References: 1 2 3 4 5 6 7 8  Groups: php.internals 
Request: Send a blank email to internals+get-108753@lists.php.net to get a copy of this message
Le lun. 24 févr. 2020 à 21:35, Larry Garfield <larry@garfieldtech..com> a écrit : > On Mon, Feb 24, 2020, at 7:55 AM, Rowan Tommins wrote: > > On Fri, 21 Feb 2020 at 23:18, Larry Garfield <larry@garfieldtech.com> > wrote: > > > > > The with*() method style requires cloning the object. What happens to > the > > > locked status of a set property if the object is cloned? Are they then > > > settable again, or do they come pre-locked? > > > > > > Neither of those seem good, now that I think about it. If they come > > > pre-locked, then you really can't clone, change one property, and > return > > > the new one (as is the standard practice now in that case). If they > don't > > > come pre-locked, then the newly created object can have everything on > it > > > changed, once, which creates a loophole. I'm not sure what the right > > > answer is here. > > > > > > > > > As with typed properties, I wonder if there's a way we can introduce a > new > > initialisation sequence for objects, so that there's a specific point > where > > the object is considered "fully constructed" after new or clone. > > > > A couple of brainstormed ideas, with plenty of downsides I'm sure: > > > > An explicit finalise() function or keyword > > > > public function withFoo($foo) { > > $inst = clone $this; > > // all readonly properties are initially "unlocked" > > $inst->foo = $foo; > > // now lock them, perhaps also checking that no typed properties are > > left uninitialised > > finalise($inst); // or finalise $inst; > > return $inst; > > } > > > > A special code block: > > > > public function withFoo($foo) { > > $inst = clone $this { > > // all properties are "unlocked" within this special block > > $inst->foo = $foo; > > }; > > // from here onwards, readonly properties can't be written to > > return $inst; > > } > > > > Perhaps could also be used with constructors: > > > > public function createFromOtherThing(OtherThing $other) { > > $inst = new static('some parameter') { > > // readonly properties can be written in the constructor, or > > within this block > > $inst->foo = $other->getFoo(); > > }; > > // object is "finalised" when the block ends > > return $inst; > > } > > If the way to resolve this question is a special "unlocked" mode, I would > definitely favor the explicit code block. That way it's self-closing and > you can't forget to do so. (Murphy's Law: If you rely on developers > remembering to do X to keep code safe, they will promptly forget to do X.) > > Also, FTR, any approach that forces developers to write a 9 parameter > constructor over and over is one I can never get behind, doubly so for > something that is currently only a single line. This isn't a case of "pain > tells you what not to do"; a value object having a lot of internal > properties is a completely valid use case, and "but composition" is not an > answer. I totally agree with this: there must be a way to work around the keyword - either via reflection or another means. Unlike private, final is an imperative keyword that cannot be bypassed, while there are very legit use cases for still extending final classes (proxies). For sure, the same will be legitimately needed with readonly. Via Reflection, it could be a new method ->setWritable(true) (next to ->setAccessible(true)). Another way, which is my current preference, would be to have visibility be taken into consideration when using the keyword: - public readonly $foo => cannot be set from the outside of the class - but can be from protected+private scopes - protected readonly $foo => can be set only from private scope - private readonly $foo => either unsupported or have the writeonce behavior defined in the RFC. This would make the keyword compatible with untyped properties and with cloning. It would provide the guarantee we need from an author pov: outside scopes cannot mess up with the state of properties. It would not provide authors a safeguard against their own mistakes - but I think that's a really secondary goal and one that is fine letting go vs the mentioned benefits. Nicolas

« previous php.internals (#108753) next »