On Thu, Jul 11, 2024, at 20:31, Tim Düsterhus wrote: > Hi > > On 7/11/24 10:32, Nicolas Grekas wrote:
... snip > > The $originalProxy is *not* shared with $clonedProxy. Instead, it's > > *initializers* that are shared between clones. > > And then, when we call that shared initializer in the $clonedProxy, we > > clone the returned instance, so that even if the initializer returns a > > shared instance, we don't share anything with the $originalProxy. > > > > Ah, so you mean if the initializer would look like this instead of > creating a fresh object within the initializer? > > $predefinedObject = new SomeObj(); > $myProxy = $r->newLazyProxy(function () use ($predefinedObject) { > return $predefinedObject; > }); > $clonedProxy = clone $myProxy; > $r->initialize($myProxy); > $r->initialize($clonedProxy); > > It didn't even occur to me that one would be able to return a > pre-existing object: I assume that simply reusing the initializer would > create a separate object and that would be sufficient to ensure that the > cloned instance would be independent. > > > > >> ? Then I believe this is unsound. Consider the following: > >> > >> $myProxy = $r->newLazyProxy(...); > >> $clonedProxy = clone $myProxy; > >> $r->initialize($myProxy); > >> $myProxy->someProp++; > >> var_dump($clonedProxy->someProp); > >> > >> The clone was created before `someProp` was modified, but it outputs the > >> value after modification! > >> > >> Also: What happens if the cloned proxy is initialized *before* the > >> original proxy? There is no real object to clone. > >> > >> I believe the correct behavior would be: Just clone the proxy and keep > >> the same initializer. Then both proxies are actually fully independent > >> after cloning, as I would expect from the clone operation. > >> > > > > That's basically what we do and what we describe in the RFC, just with the > > added lazy-clone operation on the instance returned by the initializer. > > > > This means that if I would return a completely new object within the > initializer then for a cloned proxy the new object would immediately be > cloned and the original object be destructed, yes? > > Frankly, thinking about this cloning behavior gives me a headache, > because it quickly leads to very weird semantics. Consider the following > example: > > $predefinedObject = new SomeObj(); > $initializer = function () use ($predefinedObject) { > return $predefinedObject; > }; > $myProxy = $r->newLazyProxy($initializer); > $otherProxy = $r->newLazyProxy($initializer); > $clonedProxy = clone $myProxy; > $r->initialize($myProxy); > $r->initialize($otherProxy); > $r->initialize($clonedProxy); > > To my understanding both $myProxy and $otherProxy would share the > $predefinedObject as the real instance and $clonedProxy would have a > clone of the $predefinedObject at the time of the initialization as its > real instance? > > To me this sounds like cloning an uninitialized proxy would need to > trigger an initialization to result in semantics that do not violate the > principle of least astonishment. I think it would be up to the developer writing the proxy framework to use or abuse this, for example, I've been trying for years to get some decent semantics of value objects in PHP (I may or may not create an RFC for it once I've finished all my research), but, this seems like a perfectly usable case that creates the principle of least astonishment for value objects. For example, if you have an immutable Money(10) and clone Money(10) .... is there any reason to create a new Money(10)? Currently, clone's default behavior is already astonishing for value objects! The instance doesn't matter; it's the value that matters. For service objects, it may be the same thing -- at least, IMHO, services shouldn't have state, just behavior. For non-value objects, such as those in the domain, maybe they should be fetched anew from the DB, created newly from a cache, or cloned from an existing instance. The point is, that this can have framework-level behavior that simply isn't possible right now because there is no way to control a clone operation properly. I'm actually quite excited to have some more control over cloning (even in this limited form) because the current behavior of __clone is so cobbled that it is barely usable except for the most basic of programs, and currently, the only solution is to disable cloning when it will break assumptions. — Rob