A readonly property can be set once, and then it can never change. It arrived in PHP 8.1 and makes immutable objects easy.
<?php
class Order
{
public function __construct(
public readonly int $id,
public readonly float $total,
) {}
}
$order = new Order(1042, 599.0);
echo $order->id . "\n";
try {
$order->total = 1.0;
} catch (Error $e) {
echo $e->getMessage();
}
1042
Cannot modify readonly property Order::$total
Because the property is public readonly, everyone can read it, but nobody can change it. You do not need a getter.
Rules
- Only typed properties can be
readonly. - The property can be written once, and only from inside the class.
- A readonly property cannot have a default value in its declaration (it is set in the constructor or another method).
- A readonly property cannot be
unset()once set.
<?php
class Config
{
public readonly array $values;
public function __construct(array $values)
{
$this->values = $values; // the first (and only) write
}
}
$c = new Config(["debug" => true]);
try {
$c->values["debug"] = false;
} catch (Error $e) {
echo $e->getMessage();
}
Cannot modify readonly property Config::$values
Notice that even changing inside the array is blocked. A readonly array is truly fixed.
Objects inside are still changeable
readonly protects the property, not the object it points to. If the property holds an object, that object’s own properties can change.
<?php
class Counter
{
public int $count = 0;
}
class Holder
{
public function __construct(public readonly Counter $counter) {}
}
$h = new Holder(new Counter());
$h->counter->count = 5; // allowed: it changes the inner object
echo $h->counter->count;
5
For full immutability, the inner objects must be readonly too.
Changing values: “with” methods
An immutable object cannot be edited, so you make a new copy with a different value. These methods are usually named with....
<?php
final class Money
{
public function __construct(
public readonly int $cents,
public readonly string $currency = "INR",
) {}
public function withCents(int $cents): static
{
return new static($cents, $this->currency);
}
}
$a = new Money(500);
$b = $a->withCents(750);
echo $a->cents . " " . $b->cents;
500 750
Why use readonly
- No surprises. A value cannot change behind your back.
- Safe sharing. You can pass an object around freely.
- Less code. No getters, no setters.
Cloning
clone copies a readonly object, but you cannot change the readonly properties of the copy afterwards (before PHP 8.3, not even inside __clone()). PHP 8.3 relaxed this slightly for deep copies inside __clone(). PHP 8.5 adds a clone form that accepts new values.
Use readonly by default
readonly properties. Remove readonly only for a property that really must change.