> > > 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