Le lun. 24 févr. 2020 à 21:35, Larry Garfield <la...@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 <la...@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

Reply via email to