Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:127406 X-Original-To: internals@lists.php.net Delivered-To: internals@lists.php.net Received: from php-smtp4.php.net (php-smtp4.php.net [45.112.84.5]) by lists.php.net (Postfix) with ESMTPS id BEB2E1A00BC for ; Tue, 20 May 2025 15:44:57 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1747755768; bh=/RVWNVTuLrlxSmLgkyeCkk8mGVq/56E6LUx8x08hzm8=; h=References:In-Reply-To:From:Date:Subject:To:Cc:From; b=F/K7HS0cFy5/U3XsR+dA/+HTCL6OW/PLDTMtCJ1ucNIIAMNBg7Lof2yH152NdXKzA mzrhM3y+DRIJvB64i1FlNW/6jC1stlvxTMK/bpeoW2WNxxsBo9gN1cY1iul7iYy0ba HBnNWOyAKo+rqhhCKYK6UpqkgvqldLllOa5wROJ18kJQ48tlyPNplVbUoPAoDqQ/5g 5igGSRbB3HHnQQEDYGpffuiAeDxAkbBmvxQBzqmrBlNLPeyISp3g23ioztmknK9HXn 3VlLjd0EAbQMSG/q2otM6bG3i1KMq6o559EL20rceFGiLP33mSHqOdLhk2PPVgqkFG 6dSsI1yX6RTxQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id 1506218006A for ; Tue, 20 May 2025 15:42:47 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: * X-Spam-Status: No, score=1.6 required=5.0 tests=BAYES_50,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, FREEMAIL_REPLY,HTML_MESSAGE,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2, SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: Error (Cannot connect to unix socket '/var/run/clamav/clamd.ctl': connect: Connection refused) X-Envelope-From: Received: from mail-lf1-f46.google.com (mail-lf1-f46.google.com [209.85.167.46]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Tue, 20 May 2025 15:42:46 +0000 (UTC) Received: by mail-lf1-f46.google.com with SMTP id 2adb3069b0e04-551f0072119so2920621e87.0 for ; Tue, 20 May 2025 08:44:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1747755894; x=1748360694; darn=lists.php.net; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:from:to:cc:subject:date:message-id:reply-to; bh=cKExc9QqjR4MVa6brenyn4lQESqTRXWmPtgD7GnBjtM=; b=DX0U16/6Fwxe7FGLwYLCINx7XgNhPpzm/i4ArsEfZAkPk5yu3oGL8ldyHARQaIqpY2 0HgXejS41+yiYg7/PW+wTRZ2a7Aqsoy6rHampB3Eu5e1V8xNegEVNebPfU6Mjf1KRdah qW2uJtC8Uu7nv51PfulWzKCL7Y3jqaY+1CmGhNydmyLK5Kqg/E6hx3zgtYePgt+IdBzy TF9v1Um+yvhG4otVdSmC+WrN7Rfc9+znVJxyIwJY+kwrqhFpTYSP+Qb2u2Xa7cDYCWCJ GiGCmp5YVIJx5WihfIj0B+u+Y57oI64eeYMruLCRTZCHxL5b/21jGIIiDZgQ38BnvmCu ixZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1747755894; x=1748360694; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=cKExc9QqjR4MVa6brenyn4lQESqTRXWmPtgD7GnBjtM=; b=sgwS7DgmnaWJ9395NcUslReK96utAEt9ioOK+0L4pNw3/K5xxPwEdS/SdSjm/izQKD nIVR98uaFC7bka9MSEEAPIXkOZ+pjVdk+AHIfRwmxw56kC8i27lswlVoROFQR3+FuxQx hBQRCwO6jGDJaHxysAPWDA12O20hNpMFICLiThSGbpdvhsr0ysoqNXIcWbB3oXn/5W1i 0qO9fWkSHDJwCtZjnOCm0nSj9TfCaRAt1JAGgauTj00xyOPqq6mTRN4L+RhSFN23Dazo rYzdzHfEsT1YVBFJFBN8Z1GS7H7ea+iSTLMQMyRxBhMmQxTbk/h/pgIg+BAVdmN6mSKA bhjA== X-Forwarded-Encrypted: i=1; AJvYcCXii2GrrTCuXs+fjaT3D8cnuAoj98V1lT8dZgXikh1KIID3UPwjHw8xVYj0LZWIYTmADwxhaf3uBn4=@lists.php.net X-Gm-Message-State: AOJu0YzA3tw+zHnfgQ5j66xIkBHVK2NCFPLaON3ZGRMxOqQ+6FoN6s5A 92RPyQkPk5/vHGJGtoEyRPdbsJujck6wanD3EBTOOBuuXE6ZYUTHtYut9F9zMGwNpdoe+gupbPW MQyX3XMBWrQ9ssyHd40PI8GC0ynUYE0WDZJmKpuA= X-Gm-Gg: ASbGncszZTvRpDfoecL5uKomXsSj/eLu9nZV01RfvgBQpLhj8MOo38gH0luYsK15JZ2 rzgcnWLIcZeqSwNeAdlyYaO1jRMCqA5ywVZSkxDYfblDwt96aQofuXp2CeQ2uUllGFO8LCWSV9J zHhQiegif/iaZI3z1ja5pI2JU6hxj1anB9YA== X-Google-Smtp-Source: AGHT+IETyAL8CB0Fj+JuXo/uCeAwGzGAONLrddVee8XVH9ZR9ZV2++1G8lArMpOgNE/Ahb93wmcFhugJTTxv7TzjcXo= X-Received: by 2002:a05:6512:6301:b0:550:e8b6:6996 with SMTP id 2adb3069b0e04-550e8b66bf9mr4714438e87.2.1747755893608; Tue, 20 May 2025 08:44:53 -0700 (PDT) Precedence: bulk list-help: list-post: List-Id: internals.lists.php.net x-ms-reactions: disallow MIME-Version: 1.0 References: In-Reply-To: Date: Tue, 20 May 2025 17:44:41 +0200 X-Gm-Features: AX0GCFuWJPdiEli5cd372hTm3v3_pM44j1c81hDEQNZ-OjWw3MmYgl26O9UlaUA Message-ID: Subject: Re: [PHP-DEV] [RFC] Clone with v2 To: Andreas Hennings Cc: Larry Garfield , php internals Content-Type: multipart/alternative; boundary="00000000000031533906359321aa" From: nicolas.grekas+php@gmail.com (Nicolas Grekas) --00000000000031533906359321aa Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Le lun. 19 mai 2025 =C3=A0 19:06, Andreas Hennings a =C3=A9crit : > On Mon, 19 May 2025 at 17:13, Nicolas Grekas > wrote: > > > > > > > > Le lun. 19 mai 2025 =C3=A0 16:30, Andreas Hennings a > =C3=A9crit : > >> > >> On Fri, 16 May 2025 at 21:59, Nicolas Grekas > >> wrote: > >> > > >> > > >> > > >> > Le jeu. 15 mai 2025 =C3=A0 16:06, Larry Garfield > a =C3=A9crit : > >> >> > >> >> On Thu, May 15, 2025, at 1:22 AM, Stephen Reay wrote: > >> >> > >> >> > I may be missing something here.. > >> >> > > >> >> > So far the issues are "how do we deal with a parameter for the > actual > >> >> > object, vs new properties to apply", "should __clone be called > before > >> >> > or after the changes" and "this won't allow regular readonly > properties > >> >> > to be modified". > >> >> > > >> >> > Isn't the previous suggestion of passing the new property argumen= ts > >> >> > directly to the __clone method the obvious solution to all three > >> >> > problems? > >> >> > > >> >> > There's no potential for a conflicting property name, the > developer can > >> >> > use the new property values in the order they see fit relative to > the > >> >> > logic in the __clone call, and it's inherently in scope to write > to any > >> >> > (unlocked during __clone) readonly properties. > >> >> > >> >> I did some exploratory design a few years ago on this front, lookin= g > at the implications of different possible syntaxes. > >> >> > >> >> > https://peakd.com/hive-168588/@crell/object-properties-part-2-examples > >> >> > >> >> What that article calls "initonly" is essentially what became > readonly. The second example is roughly what this RFC would look like if > the extra arguments were passed to __clone(). As noted in the article, t= he > result is absolutely awful. > >> >> > >> >> Auto-setting the values while using the clone($object, ...$args) > syntax is the cleanest solution. Given that experimentation, I would not > support an implementation that passes args to __clone and makes the > developer figure it out. That just makes a mess. > >> >> > >> >> Rob makes a good point elsewhere in thread that running __clone() > afterward is a way to allow the object to re-inforce validation if > necessary. My concern is whether the method knows it needs to do the ext= ra > validation or not, since it may be arbitrarily complex. It would also > leave no way to reject the changes other than throwing an exception, thou= gh > in fairness the same is true of set hooks. Which also begs the question = of > whether a set hook would be sufficient that __clone() doesn't need to do > extra validation? At least in the typical case? > >> >> > >> >> One possibility (just brainstorming) would be to update first, then > call __clone(), but give clone a new optional arg that just tells it what > properties were modified by the clone call. It can then recheck just tho= se > properties or ignore it entirely, as it prefers. If that handles only > complex cases (eg, firstName was updated so the computed fullName needs t= o > be updated) and set hooks handle the single-property ones, that would > probably cover all bases reasonably well. > >> > > >> > > >> > I like where this is going but here is a variant that'd be even more > capable: > >> > > >> > we could pass the original object to __clone. > >> > >> My proposal earlier was to pass the original object _and_ the values > >> that were passed to the clone call, by reference. > >> > >> And this would happen before those values are assigned to the object. > >> > >> class MyClass { > >> public function __construct( > >> public readonly int $x, > >> public readonly int $y, > >> public readonly int $z, > >> ) {} > >> public function __clone(object $original, array &$values): void { > >> // Set a value directly, and modify it. > >> if (isset($values['x'])) { > >> $this->x =3D $values['x'] * 10; > >> // Prevent that the same property is assigned again. > >> unset($values['x']); > >> } > >> } > >> } > >> > >> $obj =3D new C(5, 7, 9); > >> $clone =3D clone($obj, x: 2, y: 3); > >> assert($clone->x =3D=3D=3D 20); // x was update in __clone(). > >> assert($clone->y =3D=3D=3D 3); // y was auto-updated after __clone(). > >> assert($clone->z =3D=3D=3D 9); // z was not touched at all. > > > > > > I'm not sure I understand, there might be missing bits to your idea, eg > where is visibility enforced? why is pass-by-ref needed at all? > > Pass-by-ref makes me think this is a bad idea already :) > > Maybe we are looking at different problems to be solved. > To me, the main questions are: > - Could a __clone() method want to behave differently depending which > property values are passed with the clone call? > Definitely yes, at least to skip triggering a costly deep cloning operation= . - Can there be conflicts between operations we would normally do in > __clone() and values passed to __clone()? > I don't see any. BTW, I had a look at what is done within __clone methods in Symfony, and all implementations fall down into 4 categories: - incrementing some counter for tracking management purposes - resetting the state of the clone (at least for some transient properties) - deep-cloning - forbidding clone at all (throwing or making the method private) > - Should we prevent or allow double-write to a readonly property? That > is, if one write happens in __clone(), and the other write happens > automatically due to the property value passed to clone(..). > This question doesn't really make sense IMHO. What matters is readonly semantics, which must be preserved. The fact that there are two or more steps to achieve the target state doesn't matter. > And to clarify my proposal: > - Everything is the same as in the RFC (except points below) > - Same as in the RFC, the __clone() method is called _after_ the > original object values have been copied over, but _before_ any of the > property values passed as arguments to clone($obj, ...$values) are > assigned. > - Same as in the RFC, the values passed to clone($obj, ...$values) are > assigned automatically after the __clone() method. > - Unlike the RFC, the __clone() method can see (and validate) the > values that were passed to __clone($obj, ...$values) > - Unlike the RFC, the __clone() method can _alter_ the values passed > to __clone($obj, ...$values) before they are assigned. > - As in the RFC, readonly properties can be written only once on > clone. The __clone() method can prevent a double write by unsetting > that key in $values. > Thanks for the clarification. About this last item: the RFC has been updated on this topic. Also preventing double-writes by unsetting a by-ref array looks terrible, no chance this can be the best API, sorry :) > Consequence: > By leaving the __clone() method empty, all values are assigned > automatically, as in the RFC. > > > where is visibility enforced? > > Exactly as in the RFC. > When clone($obj, ...$values) is called, and before __clone() is > invoked, php has to verify which of the properties are legal to be > updated in this way, based on property visibility and readonly status, > and depending on the scope from which it is called. > > Actually this raises some questions that I did not think of before: > - After __clone() is invoked, does php need to validate again? > - What happens to private properties that are not accessible from the > scope of the __clone() method? Are they also passed in the $values > array? > That's definitely an issue with any approach that relies on passing property names. This doesn't happen of course if we pass only the original object and rely on =3D=3D=3D to know what changed. > > > Also, WDYT of my simpler proposal itself? Wouldn't it cover all use > cases? > > First, I want to make sure I understand correctly: > - Unlike the RFC, we want to call __clone() _after_ the values from > clone($obj, ...$values) are assigned. > (I assume this because otherwise the two objects would be identical, > and the original object would be useless) > - Unlike the RFC, we pass the original object as a parameter to __clone()= . > You've got it right, yes. > > Tbh I am not sure if the use cases I think of are relevant or not :) > I mostly think of it in terms of functional completeness, without > trying to speculate why a developer would want to do this or that > during __clone(). > > Let's look at the "benefits" section from your earlier mail. > > > The benefits I see: > > - Allow implementing this validation logic you're talking about. > > Having __clone() called after the values are assigned, as you propose, > makes it possible to run an integrity check on the object itself. On > the other hand, this may leave us with a short moment of possibly > "bad" property values. > Having __clone() called before the values are assigned means we have > to validate the values array, not the object. > This concern matters only if that temporary state is observable from the outside. Which isn't going to be the case. > > - Allow to skip deep-cloning of already updated properties (that's a > significant drawback of the current proposal - deep cloning before settin= g > is a potential perf/mem hog built into the engine) : guarding deep-clonin= g > with a strict comparison would be enough. > > With __clone() called after the values are assigned, and with access > to the original object, we can check $this->prop =3D=3D=3D $old->prop to = see > whether a specific property was updated (and therefore should not be > deep cloned). > With __clone() called before the values are assigned, and with access > to the values array, we would check isset($values['prop']) instead. > (or really array_key_exists()) > In general this would produce the same result, unless a property was > assigned the same value that it had before. > Another question is about "dependent properties", e.g. for lazily > filled calculated values. > With access to the old object we would have to check $this->prop !=3D=3D > $old->prop to then see which dependent properties need to be reset or > recalculated. > With access to the values we would check isset($values['prop']) instead. > > > - Allow implementing WeakMap that are able to clone weak-properties as > objects are cloned. > > I don't feel informed or qualified to talk about this one.. > > So, with __clone() called after and with the original object, we > compare old and new when looking for a specific property. > With access to an array of updated (or to be updated) properties, we > could iterate over the changes, or we could check whether the > changelist is empty, which is less obvious to do by comparing old and > new instance. > I think the above concern with private properties and the strange by-ref API both rule out the approach. The one I propose actually works and is simpler. > Note that I don't see the need for operations like in your example > (transforming a value while cloning). > > This looks like the job of a setter or a hook instead, not a cloner. > > Tbh, most of the classes I wrote that would benefit from a "clone > with" did not really need a __clone() method, because everything in > there was already immutable. > To me, the scenarios where we want both are quite speculative, so this > is why my examples might not be the most realistic. > Think deep-cloning. This does require __clone + has to support clone-with. >> > The benefits I see: > >> > - Allow implementing this validation logic you're talking about. > >> > - Allow to skip deep-cloning of already updated properties (that's a > significant drawback of the current proposal - deep cloning before settin= g > is a potential perf/mem hog built into the engine) : guarding deep-clonin= g > with a strict comparison would be enough. > >> > - Allow implementing WeakMap that are able to clone weak-properties > as objects are cloned. > >> > > >> > On this last aspect, I think it's new to the discussion but it's > something I've always found very limiting when using weak-map: let's say > some metadata are stored about object $foo in a weakmap, it's currently n= ot > possible to track those metadata across clones without using some nasty > tricks. If __clone were given the original object, it's be easy to > duplicate meta-data from $foo to $clone. > >> > > >> > I have just one concern significant with adding an argument to > __clone: it'dbe a BC break to mandate this argument at the declaration > level, and adding one right now generates an error with current versions = of > PHP. > >> > However, I think we could (and should if confirmed) provide some > FC/BC layer by allowing one to use func_get_args() in __clone. The engine > could then verify at compile time that __clone has either one non-nullabl= e > "object" argument, or zero. > >> > >> This seems reasonable. > >> > > > > I'd be happy to know what Volker and Tim think about this? I read they > excluded any change to __clone in the RFC, but I think it should still be > possible to discuss this, especially if it provides the path to the desir= ed > solution. > Nicolas --00000000000031533906359321aa Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable


Le=C2=A0lun. 19= mai 2025 =C3=A0=C2=A019:06, Andreas Hennings <andreas@dqxtech.net> a =C3=A9crit=C2=A0:
On Mon, 19 May 2025 at 17:13,= Nicolas Grekas
<nic= olas.grekas+php@gmail.com> wrote:
>
>
>
> Le lun. 19 mai 2025 =C3=A0 16:30, Andreas Hennings <andreas@dqxtech.net> a =C3= =A9crit :
>>
>> On Fri, 16 May 2025 at 21:59, Nicolas Grekas
>> <nicolas.grekas+php@gmail.com> wrote:
>> >
>> >
>> >
>> > Le jeu. 15 mai 2025 =C3=A0 16:06, Larry Garfield <larry@garfieldtech.com= > a =C3=A9crit :
>> >>
>> >> On Thu, May 15, 2025, at 1:22 AM, Stephen Reay wrote:
>> >>
>> >> > I may be missing something here..
>> >> >
>> >> > So far the issues are "how do we deal with a pa= rameter for the actual
>> >> > object, vs new properties to apply",=C2=A0 &quo= t;should __clone be called before
>> >> > or after the changes" and "this won't = allow regular readonly properties
>> >> > to be modified".
>> >> >
>> >> > Isn't the previous suggestion of passing the new= property arguments
>> >> > directly to the __clone method the obvious solution = to all three
>> >> > problems?
>> >> >
>> >> > There's no potential for a conflicting property = name, the developer can
>> >> > use the new property values in the order they see fi= t relative to the
>> >> > logic in the __clone call, and it's inherently i= n scope to write to any
>> >> > (unlocked during __clone) readonly properties.
>> >>
>> >> I did some exploratory design a few years ago on this fro= nt, looking at the implications of different possible syntaxes.
>> >>
>> >> https://peak= d.com/hive-168588/@crell/object-properties-part-2-examples
>> >>
>> >> What that article calls "initonly" is essential= ly what became readonly.=C2=A0 The second example is roughly what this RFC = would look like if the extra arguments were passed to __clone().=C2=A0 As n= oted in the article, the result is absolutely awful.
>> >>
>> >> Auto-setting the values while using the clone($object, ..= .$args) syntax is the cleanest solution.=C2=A0 Given that experimentation, = I would not support an implementation that passes args to __clone and makes= the developer figure it out.=C2=A0 That just makes a mess.
>> >>
>> >> Rob makes a good point elsewhere in thread that running _= _clone() afterward is a way to allow the object to re-inforce validation if= necessary.=C2=A0 My concern is whether the method knows it needs to do the= extra validation or not, since it may be arbitrarily complex.=C2=A0 It wou= ld also leave no way to reject the changes other than throwing an exception= , though in fairness the same is true of set hooks.=C2=A0 Which also begs t= he question of whether a set hook would be sufficient that __clone() doesn&= #39;t need to do extra validation?=C2=A0 At least in the typical case?
>> >>
>> >> One possibility (just brainstorming) would be to update f= irst, then call __clone(), but give clone a new optional arg that just tell= s it what properties were modified by the clone call.=C2=A0 It can then rec= heck just those properties or ignore it entirely, as it prefers.=C2=A0 If t= hat handles only complex cases (eg, firstName was updated so the computed f= ullName needs to be updated) and set hooks handle the single-property ones,= that would probably cover all bases reasonably well.
>> >
>> >
>> > I like where this is going but here is a variant that'd b= e even more capable:
>> >
>> > we could pass the original object to __clone.
>>
>> My proposal earlier was to pass the original object _and_ the valu= es
>> that were passed to the clone call, by reference.
>>
>> And this would happen before those values are assigned to the obje= ct.
>>
>> class MyClass {
>>=C2=A0 =C2=A0public function __construct(
>>=C2=A0 =C2=A0 =C2=A0public readonly int $x,
>>=C2=A0 =C2=A0 =C2=A0public readonly int $y,
>>=C2=A0 =C2=A0 =C2=A0public readonly int $z,
>>=C2=A0 =C2=A0) {}
>>=C2=A0 =C2=A0public function __clone(object $original, array &$= values): void {
>>=C2=A0 =C2=A0 =C2=A0// Set a value directly, and modify it.
>>=C2=A0 =C2=A0 =C2=A0if (isset($values['x'])) {
>>=C2=A0 =C2=A0 =C2=A0 =C2=A0$this->x =3D $values['x'] * 1= 0;
>>=C2=A0 =C2=A0 =C2=A0 =C2=A0// Prevent that the same property is ass= igned again.
>>=C2=A0 =C2=A0 =C2=A0 =C2=A0unset($values['x']);
>>=C2=A0 =C2=A0 =C2=A0}
>>=C2=A0 =C2=A0}
>> }
>>
>> $obj =3D new C(5, 7, 9);
>> $clone =3D clone($obj, x: 2, y: 3);
>> assert($clone->x =3D=3D=3D 20);=C2=A0 // x was update in __clon= e().
>> assert($clone->y =3D=3D=3D 3);=C2=A0 // y was auto-updated afte= r __clone().
>> assert($clone->z =3D=3D=3D 9);=C2=A0 // z was not touched at al= l.
>
>
> I'm not sure I understand, there might be missing bits to your ide= a, eg where is visibility enforced? why is pass-by-ref needed at all?
> Pass-by-ref makes me think this is a bad idea already :)

Maybe we are looking at different problems to be solved.
To me, the main questions are:
- Could a __clone() method want to behave differently depending which
property values are passed with the clone call?

Definitely yes, at least to skip triggering a costly deep cloning = operation.


- Can there be conflicts between operations we would normally do in
__clone() and values passed to __clone()?

I don't see any. BTW, I had a look at what is done within __clone me= thods in Symfony, and all implementations fall down into 4 categories:
- incrementing some counter for tracking management purposes
- re= setting the state of the clone (at least for some transient properties)
= - deep-cloning
- forbidding clone at all (throwing or making the method = private)

=C2=A0
- Should we prevent or allow double-write to a readonly property? That
is, if one write happens in __clone(), and the other write happens
automatically due to the property value passed to clone(..).

This question doesn't really make sense IMHO. Wha= t matters is readonly semantics, which must be preserved. The fact that th= ere are two or more steps to achieve the target state doesn't matter.

=C2=A0
And to clarify my proposal:
- Everything is the same as in the RFC (except points below)
- Same as in the RFC, the __clone() method is called _after_ the
original object values have been copied over, but _before_ any of the
property values passed as arguments to clone($obj, ...$values) are
assigned.
- Same as in the RFC, the values passed to clone($obj, ...$values) are
assigned automatically after the __clone() method.
- Unlike the RFC, the __clone() method can see (and validate) the
values that were passed to __clone($obj, ...$values)
- Unlike the RFC, the __clone() method can _alter_ the values passed
to __clone($obj, ...$values) before they are assigned.
- As in the RFC, readonly properties can be written only once on
clone. The __clone() method can prevent a double write by unsetting
that key in $values.

Thanks for the cla= rification.
About this last item: the RFC has been updated on thi= s topic.
Also preventing double-writes by unsetting a by-ref arra= y looks terrible, no chance this can be the best API, sorry :)
=C2=A0
Consequence:
By leaving the __clone() method empty, all values are assigned
automatically, as in the RFC.

> where is visibility enforced?

Exactly as in the RFC.
When clone($obj, ...$values) is called, and before __clone() is
invoked, php has to verify which of the properties are legal to be
updated in this way, based on property visibility and readonly status,
and depending on the scope from which it is called.

Actually this raises some questions that I did not think of before:
- After __clone() is invoked, does php need to validate again?
- What happens to private properties that are not accessible from the
scope of the __clone() method? Are they also passed in the $values
array?

That's definitely an issue w= ith any approach that relies on passing property names.
This does= n't happen of course if we pass only the original object and rely on = =3D=3D=3D to know what changed.
=C2=A0

>
> Also, WDYT of my simpler proposal itself? Wouldn't it cover all us= e cases?

First, I want to make sure I understand correctly:
- Unlike the RFC, we want to call __clone() _after_ the values from
clone($obj, ...$values) are assigned.
=C2=A0 (I assume this because otherwise the two objects would be identical,=
and the original object would be useless)
- Unlike the RFC, we pass the original object as a parameter to __clone().<= br>

You've got it right, yes.

=C2=A0

Tbh I am not sure if the use cases I think of are relevant or not :)
I mostly think of it in terms of functional completeness, without
trying to speculate why a developer would want to do this or that
during __clone().

Let's look at the "benefits" section from your earlier mail.<= br>
> The benefits I see:
> - Allow implementing this validation logic you're talking about.
Having __clone() called after the values are assigned, as you propose,
makes it possible to run an integrity check on the object itself. On
the other hand, this may leave us with a short moment of possibly
"bad" property values.
Having __clone() called before the values are assigned means we have
to validate the values array, not the object.

This concern matters only if that temporary state is observable= from the outside. Which isn't going to be the case.

=C2=A0
> - Allow to skip deep-cloning of already updated properties (that's= a significant drawback of the current proposal - deep cloning before setti= ng is a potential perf/mem hog built into the engine) : guarding deep-cloni= ng with a strict comparison would be enough.

With __clone() called after the values are assigned, and with access
to the original object, we can check $this->prop =3D=3D=3D $old->prop= to see
whether a specific property was updated (and therefore should not be
deep cloned).
With __clone() called before the values are assigned, and with access
to the values array, we would check isset($values['prop']) instead.=
(or really array_key_exists())
In general this would produce the same result, unless a property was
assigned the same value that it had before.
Another question is about "dependent properties", e.g. for lazily=
filled calculated values.
With access to the old object we would have to check $this->prop !=3D=3D=
$old->prop to then see which dependent properties need to be reset or recalculated.
With access to the values we would check isset($values['prop']) ins= tead.

> - Allow implementing WeakMap that are able to clone weak-properties as= objects are cloned.

I don't feel informed or qualified to talk about this one..

So, with __clone() called after and with the original object, we
compare old and new when looking for a specific property.
With access to an array of updated (or to be updated) properties, we
could iterate over the changes, or we could check whether the
changelist is empty, which is less obvious to do by comparing old and
new instance.

I think the above concern= with private properties and the strange by-ref API both rule out the appro= ach.
The one I propose actually works and is simpler.
= =C2=A0

>> > The benefits I see:
>> > - Allow implementing this validation logic you're talking= about.
>> > - Allow to skip deep-cloning of already updated properties (t= hat's a significant drawback of the current proposal - deep cloning bef= ore setting is a potential perf/mem hog built into the engine) : guarding d= eep-cloning with a strict comparison would be enough.
>> > - Allow implementing WeakMap that are able to clone weak-prop= erties as objects are cloned.
>> >
>> > On this last aspect, I think it's new to the discussion b= ut it's something I've always found very limiting when using weak-m= ap: let's say some metadata are stored about object $foo in a weakmap, = it's currently not possible to track those metadata across clones witho= ut using some nasty tricks. If __clone were given the original object, it&#= 39;s be easy to duplicate meta-data from $foo to $clone.
>> >
>> > I have just one concern significant with adding an argument t= o __clone: it'dbe a BC break to mandate this argument at the declaratio= n level, and adding one right now generates an error with current versions = of PHP.
>> > However, I think we could (and should if confirmed) provide s= ome FC/BC layer by allowing one to use func_get_args() in __clone. The engi= ne could then verify at compile time that __clone has either one non-nullab= le "object" argument, or zero.
>>
>> This seems reasonable.
>>
>
> I'd be happy to know what Volker and Tim think about this? I read = they excluded any change to __clone in the RFC, but I think it should still= be possible to discuss this, especially if it provides the path to the des= ired solution.

Nicolas
--00000000000031533906359321aa--