Наследование — один из трёх китов объектно-ориентированного программирования. Оно позволяет выстраивать цепочки классов, в которых дочерний класс получает всё, что умеет родительский, и может дополнять или изменять это по своему усмотрению. В PHP наследование реализовано через ключевое слово extends и обладает рядом важных нюансов, которые стоит знать, чтобы не попасть в ловушку.
Что такое наследование и зачем оно нужно
Представьте, что вы разрабатываете систему уведомлений. У каждого типа уведомления есть общие черты: получатель, текст сообщения, дата отправки. Но способ доставки отличается: email, SMS, push-уведомление. Без наследования вам пришлось бы писать эти общие части заново для каждого класса. Наследование решает эту проблему: общий код живёт в одном месте — родительском классе, а дочерние классы берут его бесплатно и добавляют лишь своё уникальное поведение.
class Notification
{
public function __construct(
protected string $recipient,
protected string $body,
protected \DateTimeImmutable $sentAt = new \DateTimeImmutable()
) {}
public function getSummary(): string
{
return "Кому: {$this->recipient} | {$this->sentAt->format('d.m.Y H:i')}";
}
}
// EmailNotification наследует всё из Notification
class EmailNotification extends Notification
{
public function __construct(
string $recipient,
string $body,
private string $smtpServer = 'smtp.example.com'
) {
parent::__construct($recipient, $body);
}
public function send(): void
{
echo "Email → {$this->recipient} через {$this->smtpServer}\n";
}
}
$n = new EmailNotification('user@mail.ru', 'Ваш заказ принят');
echo $n->getSummary(); // метод унаследован от Notification
$n->send(); // Email → user@mail.ru через smtp.example.com
Переопределение функционала
Дочерний класс вправе объявить метод с тем же именем, что и в родителе. PHP будет вызывать версию дочернего класса — это называется переопределением, или overriding. Важное условие: сигнатура переопределённого метода должна быть совместима с родительской: нельзя добавить обязательные параметры или изменить тип возвращаемого значения на несовместимый.
class Notification
{
public function format(): string
{
return "[Уведомление] {$this->body}";
}
}
class UrgentNotification extends Notification
{
// Переопределяем метод format() — PHP вызовет эту версию
public function format(): string
{
return "???? [СРОЧНО] {$this->body}";
}
}
$usual = new Notification('admin', 'Плановое обновление');
$urgent = new UrgentNotification('admin', 'Сервер упал!');
echo $usual->format(); // [Уведомление] Плановое обновление
echo $urgent->format(); // ???? [СРОЧНО] Сервер упал!
Вызов родительского функционала
Переопределение не всегда означает полную замену. Часто нужно расширить поведение родителя, а не выбросить его. Для этого PHP предоставляет конструкцию parent:: — она позволяет явно вызвать метод или конструктор из родительского класса прямо внутри дочернего.
Это особенно важно в конструкторах: если родитель инициализирует свойства, а дочерний класс не вызовет parent::__construct(), эти свойства так и останутся пустыми.
class Notification
{
public function __construct(
protected string $recipient,
protected string $body
) {
echo "Notification создан\n";
}
public function format(): string
{
return "[{$this->recipient}] {$this->body}";
}
}
class LoggedNotification extends Notification
{
private array $log = [];
public function __construct(string $recipient, string $body)
{
parent::__construct($recipient, $body); // сначала инициализируем родителя
$this->log[] = 'Создан: ' . date('H:i:s');
echo "LoggedNotification создан\n";
}
public function format(): string
{
// Расширяем родительский format(), не заменяем его
$base = parent::format();
return $base . ' [запись в журнале]';
}
public function getLog(): array
{
return $this->log;
}
}
$n = new LoggedNotification('ops@team.ru', 'Деплой завершён');
// Вывод:
// Notification создан
// LoggedNotification создан
echo $n->format();
// [ops@team.ru] Деплой завершён [запись в журнале]
parent:: всегда указывает на ближайшего родителя в цепочке наследования. Если классов несколько уровней, каждый вызывает parent:: своего непосредственного предка.
0 Комментарий(я)
Зарегистрируйтесь чтобы оставить комментарий