Writing readonly on every property gets repetitive. PHP 8.2 adds a readonly class: put the keyword on the class, and every property becomes readonly.
<?php
readonly class Address
{
public function __construct(
public string $street,
public string $city,
public string $postcode,
) {}
}
$a = new Address("12 Park Road", "Pune", "411001");
echo $a->city . "\n";
try {
$a->city = "Delhi";
} catch (Error $e) {
echo $e->getMessage();
}
Pune
Cannot modify readonly property Address::$city
It is the same as writing public readonly on each property, with less noise.
Compare
<?php
// PHP 8.1
final class Point
{
public function __construct(
public readonly float $x,
public readonly float $y,
) {}
}
// PHP 8.2
readonly class Point
{
public function __construct(
public float $x,
public float $y,
) {}
}
Rules
- All properties must be typed (a readonly property needs a type).
- You cannot declare static properties in a readonly class.
- You cannot add dynamic properties (creating undeclared ones fails).
- A child class of a readonly class must also be readonly.
<?php
readonly class Temperature
{
public function __construct(public float $celsius) {}
public function toFahrenheit(): float
{
return $this->celsius * 9 / 5 + 32;
}
public function plus(float $degrees): static
{
return new static($this->celsius + $degrees);
}
}
$t = new Temperature(20);
echo $t->toFahrenheit() . " / " . $t->plus(5)->celsius;
68 / 25
Good fits
- Value objects: money, date range, coordinates, email.
- Data transfer objects (DTOs): a bundle of values that moves between layers.
- Configuration objects.
Not a good fit
- A class whose state changes over time (a cart, a counter, an account).
- Entities that are loaded and updated by an ORM that sets properties after creation.
Remember what readonly does not cover
As with readonly properties, the object inside a property can still change. See Readonly Properties.
Version
Needs PHP 8.2 or newer. On PHP 8.1, readonly class is a parse error. In that case, put readonly on each property.
Pairing with final
final. A value object that cannot change and cannot be extended is simple to reason about: final readonly class Money {}.