iTranslated by AI
Mimicking Scala's copy method in PHP using named arguments
Introduction
When you want to make an object immutable in PHP, there are situations where you might write something like return new self (...) to "return a new object without changing internal properties."
class Room
{
public function __construct(private RoomId $roomId, private Status $status, private ReservationName $name)
{
// Invariants
}
public function reserve(): self
{
// Constraints
return new self($this->roomId, $status->reserve(), $this->name);
}
}
$room = new Room(/* arguments */);
$reservedRoom = $room->reserve();
In this case, even if the only thing you want to change is the status, you need to pass all the arguments to the new self(...) constructor. As these types of methods increase, it becomes extremely redundant.
On the other hand, Scala has something called a copy method. Since I'm a PHP developer, I can't write the sample code, but I've heard that if you write something like copy(status = newValue), you can create a copy with only the specified properties changed.
Refer to the documentation for details.
If something similar could be done in PHP, the aforementioned code would look much cleaner.
What are "Named Arguments"?
This is a feature introduced in PHP 8.
Named arguments were introduced in PHP 8.0.0 as an extension of the existing positional arguments. Named arguments allow passing arguments to the function based on the parameter name, rather than the parameter position. This makes the meaning of the argument self-documenting, makes the arguments order-independent and allows skipping default values arbitrarily.
Source: PHP: Function arguments - Manual > Named Arguments
By combining "named arguments", "default argument values", and the "null coalescing operator", you can mimic Scala's copy method.
How to do it
Change the Room class as follows:
class Room
{
public function __construct(private RoomId $roomId, private Status $status, private ReservationName $name)
{
// Invariants
}
public function reserve(): self
{
// Constraints
// Changed to call copy↓
return $this->copy(status: $this->status->reserve());
}
// Added
private function copy(?RoomId $roomId = null, ?Status $status = null, ?ReservationName $name = null): self
{
return new self(
$roomId ?? $this->roomId,
$status ?? $this->status,
$name ?? $this->name
);
}
}
When you want to create a new instance, just pass the values you want to change using "named arguments," and the original properties will be used for any other arguments.
As a point of caution, if you make this copy method public, it becomes possible to create instances that ignore the constraints (= business rules) you want to express in each method. Therefore, it is desirable to keep it private.
Discussion