PHP RFC: Clone with v2
- Version: 1.0
- Date: 2015-03-30
- Author: Volker Dusch (volker@tideways-gmbh.com), Tim Düsterhus (tim@tideways-gmbh.com)
- Status: Draft
- First Published at: https://wiki.php.net/rfc/clone_with_v2
Introduction
Both readonly
properties and the clone
ing of objects are commonplace in a lot of PHP application, but these two concepts don’t fully work together in all cases.
These RFC proposes to address this issue by allowing the clone
language construct to work well with readonly
.
A common usage pattern for object cloning are “immutable value-objects” having ->withProperty
methods returning a new object with this property changed. This doesn’t work well with readonly
properties at the moment.
<?php final readonly class Response { public function __construct( public int $statusCode, public string $reasonPhrase, // ... ) {} public function withStatus($code, $reasonPhrase = ''): Response { // Only works if all properties are assignable via __construct $values = get_object_vars($this); $values['statusCode'] = $code; $values['reasonPhrase'] = $reasonPhrase; return new self( ...$values ); }
To address this, we propose a hopefully, simple change that makes clone look and mostly behave like a regular function call, making it work with current PHP users' expectations regarding syntax and semantics while respecting visibility rules.
public function withStatus($code, $reasonPhrase = ''): Response { return clone( $this, statusCode: $code, reasonPhrase: $reasonPhrase, ); }
Prior Works
This change was proposed in https://wiki.php.net/rfc/clone_with and discussed https://externals.io/message/120048
Máté, the original RFC author, dropped the RFC and had no objections to us proposing this continuation trying to address the same need.
Proposal
We propose to change clone
from a standalone keyword to a language construct that optionally accepts parenthesis and a parameter list.
For most end-user cases this would, in practice, mean that any instance of clone $x
could also be written as if there would be clone
function with the following signature:
function clone(object $object, mixed ...$updatedProperties): object {}
allowing for all the following syntax examples to be valid:
$y = clone $x; $y = clone($x); $y = clone($x, foo: $foo, bar: $bar); $y = clone($x, ...$array); $y = clone($x, ...[ "foo" => $foo, "bar" => $bar, ]);
Technical Details
- A magic
__clone
method will be called before the new properties are assigned. - Assignment of new properties happens in parameter order. As a result, should there an error during the assignment, e.g. because of property hooks, the error will be raised for the first impacted property.
- Properties assignments are made just as a regular assignment would be. Meaning all regular PHP rules apply with only the readonly state being “unlocked”.
- Visibility rules for property access are enforced
- Property hooks work as expected
- Dynamic properties respect
#[AllowDynamicProperties]
__set
works as expected
Design Goals
When re-proposing this RFC, one of our goals was to take all previous discussion into account and propose something that is small in scope and cognitive load.
This RFC explicitly rejects any BC impacting syntax choices like the proposed with
keyword suggested previously, as we don't feel the scope of the feature warrants any BC impact.
Likewise, all syntax has to exist in other parts of PHP already, to not add cognitive load to readers when they come across it.
Example
TBD (Throw order, etc)
Backward Incompatible Changes
No BC break is expected. The new syntax is optional and the old syntax will continue to work as before.
Proposed PHP Version(s)
Next PHP 8.x
RFC Impact
To SAPIs
None
To Existing Extensions
None
To Opcache
TBD. Opcache adjustment can be made if needed.
New Constants
None
php.ini Defaults
None
Open Issues
None
Unaffected PHP Functionality
Future Scope
None
Proposed Voting Choices
2/3 majority:
Vote
Patches and Tests
TBD
Implementation
TBD
References
Rejected Features
None