Hi everyone,
We just opened the vote for the "Clone with v2" RFC:
RFC: https://wiki.php.net/rfc/clone_with_v2
Discussion: https://externals.io/message/127353
PR: https://github.com/php/php-src/pull/18747
As with every RFC, a 2/3 majority is required.
Voting ends 2025-06-18 at 15:30:00 UTC.
Kind regards,
Volker
--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprint
Sitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
Le mer. 4 juin 2025 à 17:15, Volker Dusch volker@tideways-gmbh.com a
écrit :
Hi everyone,
We just opened the vote for the "Clone with v2" RFC:
RFC: https://wiki.php.net/rfc/clone_with_v2
Discussion: https://externals.io/message/127353
PR: https://github.com/php/php-src/pull/18747As with every RFC, a 2/3 majority is required.
Voting ends 2025-06-18 at 15:30:00 UTC.Kind regards,
Volker
While I appreciate the effort that went into the proposal and I value the
technical discussions we've had, I feel the transition to the voting phase
comes a bit too soon. It seemed like the conversation still had room to
evolve, and seeing the vote opened while I (and possibly others) were still
forming or refining thoughts is demotivating.
Moments like this make it clear how emotionally demanding participating on
internals can be. I believe strongly in the importance of open, respectful,
and inclusive dialogue - especially when we disagree. That’s how we move
things forward, even if it takes time.
I’m sharing this not to single anyone out, but because I care about this
space and I hope we can keep making the process more collaborative,
especially around timing and closure of discussions.
Nicolas
Le mer. 4 juin 2025 à 17:15, Volker Dusch volker@tideways-gmbh.com a écrit :
Hi everyone,
We just opened the vote for the "Clone with v2" RFC:
RFC: https://wiki.php.net/rfc/clone_with_v2
Discussion: https://externals.io/message/127353
PR: https://github.com/php/php-src/pull/18747As with every RFC, a 2/3 majority is required.
Voting ends 2025-06-18 at 15:30:00 UTC.Kind regards,
VolkerWhile I appreciate the effort that went into the proposal and I value
the technical discussions we've had, I feel the transition to the
voting phase comes a bit too soon. It seemed like the conversation
still had room to evolve, and seeing the vote opened while I (and
possibly others) were still forming or refining thoughts is
demotivating.Moments like this make it clear how emotionally demanding participating
on internals can be. I believe strongly in the importance of open,
respectful, and inclusive dialogue - especially when we disagree.
That’s how we move things forward, even if it takes time.I’m sharing this not to single anyone out, but because I care about
this space and I hope we can keep making the process more
collaborative, especially around timing and closure of discussions.Nicolas
While I support this RFC and want to see it in, I have voted no for 2 reasons.
-
The switch to an array parameter, as previously noted, is a major DX loss for unclear benefit. It's just all around a worse design, and "maybe we can change how arrays work in the future" is not an answer.
-
There was still an active discussion with Nicolas going on that seemed rather important. Opening the vote while that was still going on is, as noted above, problematic.
--Larry Garfield
On Wed, Jun 4, 2025 at 6:41 PM Larry Garfield larry@garfieldtech.com
wrote:
While I support this RFC and want to see it in, I have voted no for 2
reasons.
- The switch to an array parameter, as previously noted, is a major DX
loss for unclear benefit. It's just all-around a worse design, and "maybe
we can change how arrays work in the future" is not an answer.
It is frustrating that you claim the benefits to be unclear after multiple
long explanations, when this was thankfully unearthed during RFC discussion.
Inventing a new syntax for this specific function call is something, I
couldn't go forward with this, and I think we explained well why in the
thread.
But to sum up again for the benefits of readers: Using a ...variadic to
generate a key-value array is something php-src doesn't do anywhere
currently, and it has multiple issues and edge cases. It is making the
language more inconsistent and harder to use. Adding this special syntax
for a single function is unacceptable to me. While it looks nicer to people
who don't use or like PHP arrays much, an array is PHP's, especially
php-src's, main way of passing a list of key-value pairs.
While vibes-based language design is tempting, and I've fallen for it
initially in this RFC. I want to work with the reality of how PHP works
here and not leave all problems coming from this to the core team. The
alternative syntax would introduce extra documentation effort,
inconsistency in how PHP core functions work, and generate bug reports
stemming from the edge cases outlined. I'd rather not have the feature at
all than burden maintainers with this. Especially given how negligible the
difference is in typing effort is in the end, and how it doesn't change
static analysis or IDE support. But I know we disagree on that point.
This RFC is also leaving room for future improvement by still allowing to
add further parameters if the unforeseen need for this should come up.
Ideas around changing PHPs syntax for key-value pair lists shouldn't have
been attached to this in the first place. Extracting the ideas from this
discussion into things like #[NamedParameterOnly]
, a potential 3rd array
syntax [foo: "bar"]
people argued for, or ideas like this
https://github.com/php/php-src/pull/18613 . But none of this has a place in
clone-with as introducing a new way of defining an array here instead of
somewhere generic isn't something I feel helps PHP.
- There was still an active discussion with Nicolas going on that seemed
rather important. Opening the vote while that was still going on is, as
noted above, problematic.--Larry Garfield
We answered the concerns multiple times in the discussion, including
declaring it out of scope in the initial RFC text and explaining the issues
with adding parameters to __clone in the discussions.
This RFC also leaves these, very much out of scope for this RFC, open in
the future. They would massively increase the scope of this and require a
ton of discussion that killed the first attempt at incremental improvement
already.
So from my perspective, there was no active discussion going on as nobody
else spoke up for a week and nothing changed with Nicolas, admittedly
regrettably timed, last email. Which we also answered in detail. So I fail
to see how this problematic.
Letting the RFC peter out and die in the discussion, like so many others,
is not a desired outcome for me and as this implementation doesn't block
any future improvements or make anything worse in userland.
Regards,
Volker
--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprint
Sitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
While I support this RFC and want to see it in, I have voted no for 2 reasons.
- The switch to an array parameter, as previously noted, is a major DX loss for unclear benefit. It's just all-around a worse design, and "maybe we can change how arrays work in the future" is not an answer.
It is frustrating that you claim the benefits to be unclear after multiple long explanations, when this was thankfully unearthed during RFC discussion.
Inventing a new syntax for this specific function call is something, I couldn't go forward with this, and I think we explained well why in the thread.But to sum up again for the benefits of readers: Using a ...variadic to generate a key-value array is something php-src doesn't do anywhere currently, and it has multiple issues and edge cases. It is making the language more inconsistent and harder to use. Adding this special syntax for a single function is unacceptable to me. While it looks nicer to people who don't use or like PHP arrays much, an array is PHP's, especially php-src's, main way of passing a list of key-value pairs.
While vibes-based language design is tempting, and I've fallen for it initially in this RFC. I want to work with the reality of how PHP works here and not leave all problems coming from this to the core team. The alternative syntax would introduce extra documentation effort, inconsistency in how PHP core functions work, and generate bug reports stemming from the edge cases outlined. I'd rather not have the feature at all than burden maintainers with this. Especially given how negligible the difference is in typing effort is in the end, and how it doesn't change static analysis or IDE support. But I know we disagree on that point.
While these are valid arguments, I don't know that the other thread
had enough time to settle and agree on this array syntax.
The last time I looked at it, it still had the named arguments.
(Unfortunately I don't have permissions to see the RFC edit history,
so not sure how long ago this was changed.)
This RFC is also leaving room for future improvement by still allowing to add further parameters if the unforeseen need for this should come up.
Ideas around changing PHPs syntax for key-value pair lists shouldn't have been attached to this in the first place. Extracting the ideas from this discussion into things like
#[NamedParameterOnly]
, a potential 3rd array syntax[foo: "bar"]
people argued for, or ideas like this https://github.com/php/php-src/pull/18613 . But none of this has a place in clone-with as introducing a new way of defining an array here instead of somewhere generic isn't something I feel helps PHP.
- There was still an active discussion with Nicolas going on that seemed rather important. Opening the vote while that was still going on is, as noted above, problematic.
--Larry Garfield
We answered the concerns multiple times in the discussion, including declaring it out of scope in the initial RFC text and explaining the issues with adding parameters to __clone in the discussions.
This RFC also leaves these, very much out of scope for this RFC, open in the future. They would massively increase the scope of this and require a ton of discussion that killed the first attempt at incremental improvement already.
There is a big thing here that will narrow the possibilities for a follow-up.
"A magic __clone() method will be called before the new properties are
assigned."
The proposed change by Nicolas would require the __clone() method to
be invoked after the new properties are assigned, (and pass the
original object as a parameter).
By accepting the RFC as it is, we close the door to Nicolas' proposal,
at least once this is released to the public.
In the other thread I proposed an alternative where instead of passing
the original object as a parameter to __clone(), we are passing the
values from the "clone with" call.
This would be more suitable when __clone() is called before the values
are assigned.
However, both Nicolas and me found problems with this approach.
So from my perspective, there was no active discussion going on as nobody else spoke up for a week and nothing changed with Nicolas, admittedly regrettably timed, last email. Which we also answered in detail. So I fail to see how this problematic.
I don't see Nicolas' last email from 4 Jun being answered.
It is in fact the last email in that thread.
And one week is not very long.
Letting the RFC peter out and die in the discussion, like so many others, is not a desired outcome for me and as this implementation doesn't block any future improvements or make anything worse in userland.
As always, the RFC will block any alternative version of itself.
E.g. if we release the array version (as currently proposed), we
cannot later switch to a named arguments version.
If we release the "call __clone() before assigning values", we cannot
later switch to "call __clone() after assigning values".
So, this does indeed feel rushed.
--- Andreas
Regards,
Volker--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprintSitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
While I support this RFC and want to see it in, I have voted no for 2 reasons.
- The switch to an array parameter, as previously noted, is a major DX loss for unclear benefit. It's just all-around a worse design, and "maybe we can change how arrays work in the future" is not an answer.
It is frustrating that you claim the benefits to be unclear after multiple long explanations, when this was thankfully unearthed during RFC discussion.
Inventing a new syntax for this specific function call is something, I couldn't go forward with this, and I think we explained well why in the thread.But to sum up again for the benefits of readers: Using a ...variadic to generate a key-value array is something php-src doesn't do anywhere currently, and it has multiple issues and edge cases. It is making the language more inconsistent and harder to use. Adding this special syntax for a single function is unacceptable to me. While it looks nicer to people who don't use or like PHP arrays much, an array is PHP's, especially php-src's, main way of passing a list of key-value pairs.
While vibes-based language design is tempting, and I've fallen for it initially in this RFC. I want to work with the reality of how PHP works here and not leave all problems coming from this to the core team. The alternative syntax would introduce extra documentation effort, inconsistency in how PHP core functions work, and generate bug reports stemming from the edge cases outlined. I'd rather not have the feature at all than burden maintainers with this. Especially given how negligible the difference is in typing effort is in the end, and how it doesn't change static analysis or IDE support. But I know we disagree on that point.While these are valid arguments, I don't know that the other thread
had enough time to settle and agree on this array syntax.
The last time I looked at it, it still had the named arguments.
(Unfortunately I don't have permissions to see the RFC edit history,
so not sure how long ago this was changed.)This RFC is also leaving room for future improvement by still allowing to add further parameters if the unforeseen need for this should come up.
Ideas around changing PHPs syntax for key-value pair lists shouldn't have been attached to this in the first place. Extracting the ideas from this discussion into things like
#[NamedParameterOnly]
, a potential 3rd array syntax[foo: "bar"]
people argued for, or ideas like this https://github.com/php/php-src/pull/18613 . But none of this has a place in clone-with as introducing a new way of defining an array here instead of somewhere generic isn't something I feel helps PHP.
- There was still an active discussion with Nicolas going on that seemed rather important. Opening the vote while that was still going on is, as noted above, problematic.
--Larry Garfield
We answered the concerns multiple times in the discussion, including declaring it out of scope in the initial RFC text and explaining the issues with adding parameters to __clone in the discussions.
This RFC also leaves these, very much out of scope for this RFC, open in the future. They would massively increase the scope of this and require a ton of discussion that killed the first attempt at incremental improvement already.There is a big thing here that will narrow the possibilities for a follow-up.
"A magic __clone() method will be called before the new properties are
assigned."The proposed change by Nicolas would require the __clone() method to
be invoked after the new properties are assigned, (and pass the
original object as a parameter).
By accepting the RFC as it is, we close the door to Nicolas' proposal,
at least once this is released to the public.In the other thread I proposed an alternative where instead of passing
the original object as a parameter to __clone(), we are passing the
values from the "clone with" call.
This would be more suitable when __clone() is called before the values
are assigned.
However, both Nicolas and me found problems with this approach.
I should add to this.
Nicolas pointed out the symmetry of the arguments made around calling
__clone before vs after.
E.g. in the RFC we currently see this:
Calling __clone() afterward would mean the new properties would already be set and the old ones gone, contrasting existing behavior and thus users' expectations.
But in fact both of the following sentences are true:
- Currently, no further changes are applied to a cloned object after
__clone() is called. - Currently, no changes are applied to a cloned object before
__clone() is called.
Both of these statements can describe user expectations, but each of
them justifies a different version of the RFC.
So from my perspective, there was no active discussion going on as nobody else spoke up for a week and nothing changed with Nicolas, admittedly regrettably timed, last email. Which we also answered in detail. So I fail to see how this problematic.
I don't see Nicolas' last email from 4 Jun being answered.
It is in fact the last email in that thread.
And one week is not very long.Letting the RFC peter out and die in the discussion, like so many others, is not a desired outcome for me and as this implementation doesn't block any future improvements or make anything worse in userland.
As always, the RFC will block any alternative version of itself.
E.g. if we release the array version (as currently proposed), we
cannot later switch to a named arguments version.
If we release the "call __clone() before assigning values", we cannot
later switch to "call __clone() after assigning values".
So, this does indeed feel rushed.--- Andreas
Regards,
Volker--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprintSitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
Hi
Am 2025-06-11 22:09, schrieb Andreas Hennings:
Calling __clone() afterward would mean the new properties would
already be set and the old ones gone, contrasting existing behavior
and thus users' expectations.But in fact both of the following sentences are true:
- Currently, no further changes are applied to a cloned object after
__clone() is called.
This is only true to a very small extent, specifically for public public(set) readonly
properties.
For non-readonly properties, the user could just write into the
properties after cloning, for protected(set) readonly
, the user can
just overwrite __clone()
and for private(set) readonly
the class is
in control.
Rowan already summarized that during the discussion in
https://externals.io/message/127353#127389 and Volker confirmed that we
agreed with that.
- Currently, no changes are applied to a cloned object before
__clone() is called.Both of these statements can describe user expectations, but each of
them justifies a different version of the RFC.
Best regards
Tim Düsterhus
While these are valid arguments, I don't know that the other thread
had enough time to settle and agree on this array syntax.
The last time I looked at it, it still had the named arguments.
(Unfortunately I don't have permissions to see the RFC edit history,
so not sure how long ago this was changed.)
I think you can now, as you've created an account?
We have to require that as the version histories were absolutely hammered by bots, and it's not a cheap operation.
cheers
Derick
Hi
Am 2025-06-11 21:37, schrieb Andreas Hennings:
While these are valid arguments, I don't know that the other thread
had enough time to settle and agree on this array syntax.
The last time I looked at it, it still had the named arguments.
(Unfortunately I don't have permissions to see the RFC edit history,
so not sure how long ago this was changed.)
Volker announced the change in the discussion thread on May 26
(https://news-web.php.net/php.internals/127460, 16 days ago).
However the “named parameters vs array” question was an open question
since the very beginning of the discussion (May 14, 28 days ago) where
we specifically asked for opinions, asked again on May 19 (23 days ago)
and after we only received an opinion from Larry, I stated my own one on
May 21 (21 days ago), which Theodore then agreed with.
One June 2 (9 days ago), Volker announced the intent to open the vote
(after 5 days without any further emails,
https://news-web.php.net/php.internals/127539) for June 4 (7 days ago).
There were 21 days of discussion, for 9 of those days the change was in
the RFC text, for 14 of those the change could've been anticipated and
for 21 of those folks were able to add their opinion. I'd say this is
plenty of time for the “syntax to settle”.
In the other thread I proposed an alternative where instead of passing
the original object as a parameter to __clone(), we are passing the
values from the "clone with" call.
This would be more suitable when __clone() is called before the values
are assigned.
Yes, this is listed in the Future Scope section of the RFC.
So from my perspective, there was no active discussion going on as
nobody else spoke up for a week and nothing changed with Nicolas,
admittedly regrettably timed, last email. Which we also answered in
detail. So I fail to see how this problematic.I don't see Nicolas' last email from 4 Jun being answered.
This email arrived after the RFC vote was opened and besides clarifying
some points of Nicolas' earlier email (which I answered) only mentioned
that I did not diligently list all possible follow-ups in the “Future
Scope” section. However the future scope section is non-normative
anyways, details are figured out in the follow-up RFC if / when it
arrives. Passing both the original object and the $withProperties array
would be possible, as I mentioned in the discussion.
So, this does indeed feel rushed.
We carefully considered all the opinions voiced in the discussion and
are confident in the design of the RFC. Every RFC author has their own
vision and RFCs are naturally opinionated, building something we don’t
believe should be in PHP or would not use ourselves would not make
sense.
Best regards
Tim Düsterhus
On Wed, Jun 4, 2025 at 5:49 PM Nicolas Grekas nicolas.grekas+php@gmail.com
wrote:
While I appreciate the effort that went into the proposal and I value the
technical discussions we've had, I feel the transition to the voting phase
comes a bit too soon. It seemed like the conversation still had room to
evolve, and seeing the vote opened while I (and possibly others) were still
forming or refining thoughts is demotivating.Moments like this make it clear how emotionally demanding participating on
internals can be. I believe strongly in the importance of open, respectful,
and inclusive dialogue - especially when we disagree. That’s how we move
things forward, even if it takes time.I’m sharing this not to single anyone out, but because I care about this
space and I hope we can keep making the process more collaborative,
especially around timing and closure of discussions.Nicolas
Thank you for your concerns,
I agree. This RFC was, given how small in scope it was, a lot more
emotionally taxing than I expected going in, and looking back I'm not sure
if it was worth the effort.
Being pulled towards unrelated or out of scope ideas on and off the list
and arguing against scope and syntax creep was quite draining.
I'm very happy we reached a point in the discussion where I feel this RFC
doesn't block any future improvements or additions and can be voted without
negatively impacting anything when accepted. (You might feel different ofc).
I’m sharing this not to single anyone out, but because I care about this
space and I hope we can keep making the process more collaborative,
especially around timing and closure of discussions.
I do feel both me and Tim have been very diligent in answering every
question and concern here, so this is very frustrating and demotivating to
hear indeed.
While the timing of your last reply and our plan to start the vote collided
at bit I do not feel further discussion would have changed anything on the
matter here as we very much didn't want to touch __clone or parameters to
it in this RFC, clearly spelling this out in the rejected features.
So, for me, the best outcome was to vote and be done with this instead of
letting this peter out like the first one. Getting the feedback that is
this not respectful, or inclusive or open is difficult to understand and
painful.
Kind Regards,
Volker
--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprint
Sitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
Hi everyone,
We just opened the vote for the "Clone with v2" RFC:
RFC: https://wiki.php.net/rfc/clone_with_v2
Discussion: https://externals.io/message/127353
PR: https://github.com/php/php-src/pull/18747As with every RFC, a 2/3 majority is required.
Voting ends 2025-06-18 at 15:30:00 UTC.Kind regards,
Volker--
Volker Dusch
Head of Engineering
Tideways GmbH
Königswinterer Str. 116
53227 Bonn
https://tideways.io/imprintSitz der Gesellschaft: Bonn
Geschäftsführer: Benjamin Außenhofer (geb. Eberlei)
Registergericht: Amtsgericht Bonn, HRB 22127
I’m just now getting around to really digging into the RFC, and I’m trying to understand what problems it is solving.
"withers" as they’re often called in other languages that use "with()" and this seems to implement, are most useful in cases of value/immutable objects. In PHP, this usually looks something like this:
readonly class Money {
private function __clone() {
throw new LogicException("do not clone");
}
public function __construct(public int $pennies) {}
public function with(int $pennies) { return new self($pennies); }
}
$dollar = new Money(100);
$quarter = $dollar->with(pennies: 25);
This is an overly simple example; where we want to make sure we have a single "dollar" that would be equal to another "dollar". This implementation of clone() seems like it would be quite useful here, but it appears that setting __clone() to private would completely make this new clone() break.
So, it seems like this can only be used for things that can already be cloned? Things which are directly trying to block an identical clone, but would otherwise be fine with a clone that changes the value (e.g. value objects) aren’t allowed to use this new feature?
To me, that feels like an oversight in the design.
— Rob
Hi
Am 2025-06-08 21:15, schrieb Rob Landers:
So, it seems like this can only be used for things that can already be
cloned?
Yes. It's an extension of the existing cloning functionality.
Things which are directly trying to block an identical clone, but would
otherwise be fine with a clone that changes the value (e.g. value
objects) aren’t allowed to use this new feature?
To me, that feels like an oversight in the design.
Trying to enforce “singleton” objects to be able to ===
compare them
already requires you to take care of quite a number of things (e.g.
making the constructor private, disallowing serialization, …) and
cloning is no different in that regard.
Nevertheless you can make __clone()
private (which means that cloning
is only allowed from within the class, no Exception necessary) and then:
private function __clone() { }
public function withFoo($foo) {
if ($this->foo === $foo) {
return $this;
}
return clone($this, ['foo' => $foo]);
}
To make sure you are only creating a clone when actually changing
anything.
Best regards
Tim Düsterhus
Hi
Am 2025-06-08 21:15, schrieb Rob Landers:
So, it seems like this can only be used for things that can already be
cloned?Yes. It's an extension of the existing cloning functionality.
Things which are directly trying to block an identical clone, but would
otherwise be fine with a clone that changes the value (e.g. value
objects) aren’t allowed to use this new feature?
To me, that feels like an oversight in the design.Trying to enforce “singleton” objects to be able to
===
compare them
already requires you to take care of quite a number of things (e.g.
making the constructor private, disallowing serialization, …) and
cloning is no different in that regard.Nevertheless you can make
__clone()
private (which means that cloning
is only allowed from within the class, no Exception necessary) and then:private function __clone() { } public function withFoo($foo) { if ($this->foo === $foo) { return $this; } return clone($this, ['foo' => $foo]); }
To make sure you are only creating a clone when actually changing
anything.Best regards
Tim Düsterhus
Thank you, that makes sense.
— Rob