Le ven. 9 juin 2023 à 02:11, Larry Garfield <la...@garfieldtech.com> a écrit :
> On Thu, Jun 8, 2023, at 5:39 PM, Stephen Reay wrote: > > > Is there a specific reason `clone with (foo: $bar);` can’t simply pass > > the arguments given to with(), to the __clone() magic method? > > > > It leaves the developer free to use the passed argument(s) or deep > > clone existing properties or a mix depending on what’s passed, and > > seems like it has the least “magic” or unknown behaviour in terms of > > when things happen. > > > > Just a thought. > > I experimented with that a few years back. The results showed it is > actually pretty awful in practice. > > https://peakd.com/hive-168588/@crell/object-properties-part-2-examples > Thanks for the link. Instead of passing arguments to __clone(), I wondered about a new __clone_with(array $properties) that could be implemented next to __clone(), with the following behavior: - if only __clone is implemented, same behavior as always - if __clone_with is implemented, __clone is never called. Instead, the engine calls __clone_with with the map of properties in the "with" (or an empty array if no "with"). In addition and *before* the call, the engine sets the properties according to the "with" (potentially failing visibility/type checks as expected) Rewriting the example in your blog post Larry, the Request class would then look like below. And I think I like it. WDYT? Mate? class Request implements RequestInterface { public readonly array $headers = []; public readonly UriInterface $uri; public readonly string $version = '1.1'; public readonly string $method = 'GET'; public function __clone() { $this->uri = clone $this->uri; } public function __clone_with(array $properties) { if (isset($properties['version']) && !in_array($this->version, ['1.1', '1.0', '2.0'])) { throw new InvalidArgumentException($k); } $headers = $this->headers; if (isset($properties['uri'])) { $headers['host'] = $this->uri->host; } else { $this->uri = clone $this->uri; } $this->headers = $headers; } public function getHeader($name): string { return $this->headers[strtolower($name)] ?? ''; } // Still needed because of the $preserveHost = true option. public function withUri(UriInterface $uri, bool $preserveHost = false): static { $headers = $this->headers; // If headers were itself a pseudo-immutable object, this would be even uglier. if ($preserveHost && isset($headers['host'])) { $headers['host'] = $uri->host; } return clone $this with (uri: $uri, headers: $headers); } public function withHeader(string $name, string $value): static { $headers = $this->headers; $headers[strtolower($name)] = $value; return clone $this with (headers: $headers); } }