Для чего нужны магические методы в PHP
Что проверяют
Знаете ли вы, какие хуки даёт движок PHP и когда он их вызывает, и понимаете ли цену «магии» для читаемости и статического анализа. Следом часто спрашивают, как на этом построены Laravel и Doctrine и почему в своём коде магию лучше дозировать.
Как отвечать
Магические методы начинаются с __, напрямую их не вызывают: движок дёргает их сам. Сгруппируйте по назначению:
- жизненный цикл:
__construct,__destruct,__clone(вызывается на копии послеclone, нужен для глубокого копирования вложенных объектов); - недоступные свойства:
__get,__set,__isset,__unset. Срабатывают, только если свойства нет или оно недоступно из текущей области видимости; - недоступные методы:
__callи__callStatic. На них держатся фасады Laravel и динамическиеwhere-методы Eloquent; - преобразование:
__toString(с PHP 8 такой класс автоматически реализуетStringable),__invokeдля вызова объекта как функции; - сериализация и отладка:
__serializeи__unserialize(с PHP 7.4) заменяют__sleep,__wakeupи интерфейсSerializable,__debugInfoуправляет выводомvar_dump.
Дальше скажите о цене. Магия прячет контракт класса: IDE и PHPStan не видят таких свойств и методов, поэтому их описывают через @property и @method в PHPDoc. Ошибка в имени не упадёт сразу, а тихо уйдёт в __get. С PHP 8.2 динамические свойства без __get/__set объявлены устаревшими, а многие задачи сегодня решаются явно: readonly-свойства, продвижение параметров конструктора, enum.
Где спотыкаются
Думают, что __get вызывается при каждом обращении к свойству. Для существующего доступного свойства он не вызывается вовсе.
Забывают, что clone по умолчанию делает поверхностную копию: вложенные объекты остаются общими, пока вы не склонируете их в __clone.
Называют __get и __set способом инкапсуляции. На деле это обход явного контракта, и обычные геттеры или readonly-свойства надёжнее и понятнее.