📊 readonly properties vs private + getter
До 8.1 иммутабельность в value objects эмулировали через private + геттер. Это работало, но создавало шум: конструктор, приватное свойство, метод. Три места ради одной идеи.
// До
class Money {
private int $amount;
public function __construct(int $amount) {
$this->amount = $amount;
}
public function getAmount(): int {
return $this->amount;
}
}
readonly убирает весь этот церемониал:
// После
class Money {
public function __construct(
public readonly int $amount,
) {}
}
Читается как контракт: свойство публично, но записать его можно только один раз при инициализации. Повторная запись → Error, не предупреждение.
Когда readonly не подходит: 1. Нужна ленивая инициализация. readonly нельзя присвоить вне конструктора (до PHP 8.4 с хуками).
2. Клонирование с изменением. clone с readonly требует PHP 8.3 (readonly классы + clone в new static).
3. Моки в тестах. Если тест хочет подменить значение readonly-свойства через рефлексию, получит Error.
PHP 8.2 добавил readonly классы — все свойства автоматически readonly:
readonly class Coordinates {
public function __construct(
public float $lat,
public float $lng,
) {}
} Хорошо для DTO, value objects, команд и событий. Плохо там, где нужна мутация или частичная инициализация.
Правило простое: если объект концептуально иммутабелен → readonly. Если есть хоть одна причина менять состояние после создания → private + логика в методах.
🐸
Библиотека пхпшника#элементарный_выбор