Абстрактный класс PHP объявляется как abstract class Name, и создать его через new нельзя. Это база для других классов: у него могут быть свойства, конструктор и готовые методы, а также abstract-методы без тела, которые обязан реализовать каждый дочерний класс.
describe() написан один раз в Shape и вызывает area(), который предоставляет каждый дочерний класс. Абстрактный класс гарантирует, что такой метод существует, поэтому describe() может на него полагаться.
Абстрактный класс нельзя создать через new
Абстрактный класс намеренно неполон: у Shape::area() нет тела, поэтому простому объекту Shape нечего было бы выполнять. PHP отказывается его создавать и выбрасывает Error:
Абстрактные методы: правила
У абстрактного метода есть сигнатура и точка с запятой вместо тела. Каждый дочерний класс должен реализовать его с совместимой сигнатурой и той же или более широкой видимостью: метод abstract protected может стать в дочернем классе protected или public, но никогда private. Класс, в котором есть хотя бы один абстрактный метод, сам должен быть объявлен абстрактным.
Дочерний класс, который пропустил абстрактный метод, останавливает скрипт до того, как выполнится хоть что-то, если только он сам не объявлен абстрактным и не оставляет метод своим потомкам:
PHP Fatal error: Class Square contains 1 abstract method and must therefore be declared abstract or implement the remaining methods (Shape::area) in /home/index.php on line 3
Шаблонный метод
Самая частая причина написать абстрактный класс: фиксированная последовательность шагов, где несколько шагов отличаются. Базовый класс владеет последовательностью в методе final, а дочерние заполняют шаги. Здесь один и тот же отчёт выводится обычным текстом и HTML-таблицей:
Запустите, и вкладка Page покажет HTML-таблицу над текстовой версией. У open() и close() есть тела по умолчанию, поэтому их переопределяет только HtmlReport; row() абстрактный, потому что разумного варианта по умолчанию нет. final у render() не даёт дочернему классу поменять порядок шагов.
Абстрактный класс может реализовать интерфейс
Абстрактный класс может объявить implements и оставить часть методов интерфейса своим потомкам. Это стандартный способ сочетать интерфейс (тип) с базовым классом (общий код):
Код использует подсказку типа по интерфейсу Cache, поэтому класс, который не может наследовать BaseCache (потому что уже наследует что-то другое), всё равно может реализовать Cache напрямую.
Когда использовать абстрактный класс, а когда нет
Берите абстрактный класс, когда у классов одного семейства есть настоящий общий код: свойства, конструктор, методы, одинаковые для всех. Если в базе были бы одни абстрактные методы, это замаскированный интерфейс, и интерфейс здесь лучше, потому что класс может реализовать много интерфейсов, а наследовать только один класс. Если нужно лишь поделиться несколькими вспомогательными методами между несвязанными классами, используйте трейт, а не загоняйте их под одного родителя.
Быстрая проверка перед тем, как писать abstract class: будет ли каждый дочерний класс действительно разновидностью базы (Circle это Shape)? Если честный ответ "он умеет это", а не "он один из них", пишите интерфейс.
Часто задаваемые вопросы
Что такое абстрактный класс в PHP?
Класс, объявленный как abstract class Name, объект которого нельзя создать через new. Это база для других классов: он может содержать свойства, конструктор и обычные методы и может объявлять abstract-методы, которые обязан реализовать каждый дочерний класс.
Что такое абстрактный метод в PHP?
Метод, объявленный с abstract и без тела: abstract protected function render(array $rows): string;. Класс, который его содержит, сам должен быть объявлен abstract, а каждый неабстрактный дочерний класс должен определить метод с совместимой сигнатурой.
Может ли у абстрактного класса быть конструктор в PHP?
Да. Вызвать new для абстрактного класса нельзя, но его конструктор выполняется при создании дочернего класса: автоматически (если у дочернего нет своего конструктора) или через parent::__construct(...).
Можно ли создать объект абстрактного класса в PHP?
Нет. new Shape() для абстрактного класса выбрасывает Error: Cannot instantiate abstract class Shape. Создайте объект конкретного дочернего класса, например new Circle(2).
Когда использовать абстрактный класс в PHP?
Когда у нескольких связанных классов есть настоящий общий код (свойства, конструктор, общие методы), а различаются они несколькими шагами. Поместите общий код в абстрактный класс, а различающиеся шаги объявите абстрактными. Если общего кода нет и нужен только контракт, используйте интерфейс.