Что такое late static binding в PHP
Что проверяют
Знаете ли вы, как PHP разрешает self и static в иерархии наследования, и понимаете ли разницу между классом, где метод написан, и классом, через который его вызвали. Вопрос отделяет тех, кто писал базовые классы и фабрики, от тех, кто наследование только читал.
Как отвечать
self разрешается при компиляции и всегда указывает на класс, в теле которого написан код. static разрешается в момент вызова и указывает на класс, через который вызов пришёл. Второе и есть позднее статическое связывание.
class Model {
public static function table(): string { return 'model'; }
public static function query(): string {
return 'select * from ' . static::table();
}
public static function create(): static { return new static(); }
}
class User extends Model {
public static function table(): string { return 'users'; }
}
echo User::query(); // select * from usersЗамените static:: на self:: и получите model в обоих случаях. Рабочие формы: new static, static::method(), возвращаемый тип static, static::class. Так строятся фабрики, активные записи и базовые классы, которые обязаны создавать наследника, а не себя.
Сильный ответ добавит, что вызванный класс передаётся дальше только перенаправляющими вызовами (self::, parent::, static::, forward_static_call). Явный вызов Model::query() из наследника сбрасывает контекст обратно на Model.
Где спотыкаются
Объясняют механизм через __CLASS__ и не замечают подмены. Это константа этапа компиляции, она всегда даёт класс, где написана; во время выполнения имя вызванного класса дают static::class и get_called_class().
Считают, что static работает только в статических методах. В обычном методе он тоже указывает на реальный класс объекта, поэтому new static внутри метода экземпляра вернёт наследника.
Ставят new static() в базовом классе, не подумав о конструкторах потомков. Если наследник изменит сигнатуру конструктора, падение случится в рантайме; для таких иерархий конструктор фиксируют, а различия выносят в именованные конструкторы.