Re: [RFC] [Discussion] Clone with

From: Date: Thu, 08 Jun 2023 22:39:47 +0000
Subject: Re: [RFC] [Discussion] Clone with
References: 1  Groups: php.internals 
Request: Send a blank email to internals+get-120547@lists.php.net to get a copy of this message
Sent from my iPhone > On 9 Jun 2023, at 02:33, Larry Garfield <larry@garfieldtech.com> wrote: > > On Thu, Jun 8, 2023, at 6:15 PM, Nicolas Grekas wrote: >>> On Tue, May 30, 2023, at 10:04 PM, Alexandru Pătrănescu wrote: >>>>> On Tue, May 30, 2023, 19:39 Larry Garfield <larry@garfieldtech.com> >>>> wrote: >>>>> >>>>>>> On Mon, May 29, 2023, at 11:22 PM, Máté Kocsis wrote: >>>>>>>> To be honest, the current behavior seemed like the natural choice >>> for >>>>>>> me, and I didn't really consider to execute the __clone() method >>> after >>>>>> the >>>>>>> clone assignments. >>>>>>> Do you have a use-case in mind when you need to forward-pass >>>> information >>>>>> to >>>>>>> __clone()? >>>>>> >>>>>> Not a specific one off hand. It's more a conceptual question. >>>>>> with >>>> has >>>>>> more contextual awareness than __clone(), so it should have "first >>>> crack" >>>>>> at the operation, so that if necessary it can make changes that >>>> __clone() >>>>>> can then respond to. The inverse doesn't make sense. >>>>>> >>>>>> The only reason for with to come after >>>>>> would be to allow with to >>>>>> "override" or "undo" something that __clone() did. >>>>>> Generally >>> speaking, >>>> if >>>>>> you have to undo something you just did, you shouldn't have done it in >>>> the >>>>>> first place, so that's a less compelling combination. >>>>>> >>>>>> This one isn't a deal breaker, but we should be sure to think it >>> through >>>>>> as it's kinda hard to reverse later. >>>>>> >>>>> >>>>> To me so far also it was natural to assume that __clone is first and >>> only >>>>> after that the rest of the operations. >>>>> But with operations, be it properties >>>>> assignment or even a closure, >>>> would >>>>> run in the context of the caller of clone and sometimes this might be >>> run >>>>> not from a method of the cloned class. >>>>> >>>>> An example: >>>>> There is a class that represents persons of a fictive country/planet. >>>>> Each person has many properties but has also a first name and a last >>> name >>>>> and there is a rule: the two names must not start with the same letter.. >>>>> Both names cannot be changed as they are defined readonly. >>>>> Creation of new persons can be done using new for new random properties >>>> or >>>>> using clone to preserve existing properties. But in both cases the >>> first >>>>> name and last name are randomly chosen. >>>>> If we want to control the last name value during clone that would be >>>>> possible using the with operation but the logic >>>>> to allocate a first >>>> name >>>>> will only happen in __clone()method. >>>>> >>>>> To be able to achieve this we must have __clone last, as there we have >>>> the >>>>> internal validations, operations and also access to private/protected >>>>> members that are not accesible from where clone is being called. >>>>> >>>>> Regards, >>>>> Alex >>>> >>>> I... could not understand that in the slightest. Can you show it in >>> code? >>>> >>>> >>> >>> Sorry for that. Here you go: >>> https://3v4l.org/JIBoI/rfc#vgit.master >>> If __clone would be first, there is no way to enforce the rule that a >>> person cannot have their first name starting with the same letter as last >>> name. >>> >> >> I'm not sure that's what __clone should be used for. >> This looks like a perfect use case for property hooks, isn't it? >> >> On my side, I would find it unexpected that __clone is called after because >> that would break cloning expectations: >> Imagine you have a __clone that does some deep cloning (a much more typical >> scenario for this construct), >> Let's say __clone() does $this->foo = clone $this->foo; >> >> Imagine now that you do: clone $obj with (foo: $bar) >> I'd expect $obj->foo === $bar after this. But if __clone is called after, >> that won't be true, and I don't see how that could be "fixed" if we >> swap >> the order. Would you? >> >> Nicolas > > Oh, interesting. There's a nice example case. To make it a bit more concrete, and think > aloud... > > class Pet { > public function __construct( > public readonly string $name = 'Fluffy', > ) {} > } > > class Address { ... } > > class Person { > public function __construct( > public readonly Pet $pet, > public readonly Address $address, > } > > public function __clone() { > // Legal now thanks to a previous RFC. > $this->address = clone ($this->address); > } > } > > $p = new Person(new Pet(), new Address(blah)); > > // The person gets a new pet. > $p2 = clone $p with (pet: new Pet('Bonzo')); > > In this case, the order of operations is irrelevant. > > // The person moves. > $newAddr = new Address(whatever); > $p3 = clone $p2 with (address: $newAddr); > > In this case, if the with happens first, then the new address > object is cloned needlessly, but that *probably* doesn't hurt anything. But $newAddr !== > $p3->address. > > If the __clone() happens first, then the existing address > object is cloned needlessly, but that *probably* doesn't hurt anything. Because the assignment > happens second, $newAddr === $p3->address. > > So I suppose that's a point in favor of __clone() first. > > Another option could be to run with first, but then pass a list > of the keys that were modified (probably not their values, just the keys?) to __clone(). It can > then do conditional cloning based on that if appropriate. I... don't know if that's a > good idea or not, nor what the BC implications might be. > > Máté, your thoughts? > > --Larry Garfield > > -- > PHP Internals - PHP Runtime Development Mailing List > To unsubscribe, visit: https://www.php.net/unsub.php > Is there a specific reason clone with (foo: $bar); can’t simply pass the arguments given to with(), to the __clone() magic method? It leaves the developer free to use the passed argument(s) or deep clone existing properties or a mix depending on what’s passed, and seems like it has the least “magic” or unknown behaviour in terms of when things happen. Just a thought.

« previous php.internals (#120547) next »