> > > What about using a real closure to define the scope we need for > cloning? > > > That closure would take the cloned instance as argument to allow > > > manipulating it at will. > > > > I believe someone mentioned that one previously in the thread. > > > Yes, Nicolas mentioned me. > I wanted to get back myself to discussing this topic more as well and find > a better solution but didn't get yet time for it. > > > > The problem is that the closure would run in the scope of the object, > > not the scope of the caller. That means if called outside the object, it > > would allow modifying private or protected properties. The itemized list > > of values (whether an array or named-args style) would allow the engine > to > > enforce access restrictions, which is a desireable feature. > > > > As far as I can see, Nicolas was able to find a solution to this problem > and so far I like it: > The closure is running in the current scope where it is defined and not in > the scope of the cloned object. The cloned object is passed as the single > parameter to the closure. >
Absolutely. This would be a plain boring closure with all its current visibility semantics. Using a closure to run some code nested in a transaction is already quite a common practice. E.g.this is a common way to define the computation logic for a cache storage: $cache->get($cacheKey, $callback) Or for a database transaction: $db->transaction(function() { ... }); The cloning logic we want to run fits this style, so this is quite natural once we realize that: $clone = clone($this, $callback); The only thing we would need to settle on is the interface of the $callback. The suggested clone signature for a class T would be: > - clone(T $object, callable(T $clone)): T; // calling clone as a function > - clone T $object with callable(T $clone): T; // calling clone as a > language construct 100% this. My preference goes for #1, to keep things as boring as possible. Just to make it clear, I would document the closure with the void return type: clone(T $object, callable(T $clone)*:void*): T; // calling clone as a function > Alternatively, we can have also: > - clone T, callable(T $clone); // without "with" keyword > This would be ambiguous, eg foo(clone T, callable(T $clone)) is that a function call with two arguments, or? > And improve it to allow even multiple closures to be executed: > - clone(T $object, ...callable(T $clone)): T; > - clone T $object, ...callable(T $clone): T; > I wouldn't allow this. Calling many closures is what the main closure can do in its body, no need for more fancy things. Nicolas