Для чего нужна иммутабельность
Что проверяют
Понимаете ли вы, какие ошибки убирает неизменяемость именно в PHP, где у каждого запроса своя память и довод про потоки почти не работает. Следом просят показать, как сделать неизменяемый объект средствами языка и где эта защита неполная.
Как отвечать
Главная польза в предсказуемости. Объекты в PHP передаются по дескриптору, поэтому любой код, получивший ваш объект, может его поменять, и вы узнаете об этом только по багу. Неизменяемый объект можно отдать куда угодно.
- классический пример:
DateTime::modify()меняет сам объект, и дата, переданная в функцию, внезапно сдвигается у вызывающего.DateTimeImmutable::modify()возвращает новый экземпляр; - value objects: деньги, email, диапазон дат. Инварианты проверяются один раз в конструкторе, дальше объект всегда валиден;
- PSR-7:
withHeader()и прочиеwith-методы возвращают копию запроса, поэтому middleware не портят объект друг другу; - кеширование и сравнение: неизменяемое значение безопасно держать в кеше и переиспользовать.
Средства языка: readonly-свойства появились в PHP 8.1, readonly-классы в 8.2. Свойство обязано быть типизированным и инициализируется один раз изнутри класса. В 8.3 такие свойства разрешили переинициализировать в __clone(), что упростило with-методы, а в 8.4 появилась асимметричная видимость public private(set). Типичный with-метод клонирует $this, меняет поле и возвращает клон либо собирает новый объект через конструктор.
Честно назовите цену: каждое изменение создаёт новый объект, и в горячих циклах на больших коллекциях это лишние аллокации. А сущность с идентичностью, например заказ в Doctrine, делать неизменяемой обычно незачем.
Где спотыкаются
Главным доводом называют многопоточность. Для классического PHP-FPM это слабый аргумент, интервьюер ждёт про побочные эффекты и инварианты.
Считают readonly глубокой заморозкой. Если в свойстве лежит объект, его внутреннее состояние по-прежнему меняется, readonly защищает только саму ссылку.
Убирают сеттеры, но оставляют методы, которые меняют состояние внутри. Неизменяемость определяется поведением класса, а не отсутствием слова set в названиях.