Наследование в PHP: extends и parent
Наследование классов в PHP позволяет создавать иерархию, где дочерний класс перенимает свойства и методы родителя. Механизм extends упрощает повторное использование кода и строит полиморфные структуры.
Основы наследования с extends
Для создания класса-наследника используется ключевое слово extends. Пример:
class Animal {
public $name;
public function __construct($name) { $this->name = $name; }
public function speak() { return $this->name . ' издаёт звук'; }
}
class Dog extends Animal {
public function speak() { return $this->name . ' лает'; }
}
$dog = new Dog('Бобик');
echo $dog->speak(); // Бобик лаетPhp class extends (наследование классов в php)
Класс Dog переопределил метод speak. Если нужно дополнить, а не заменить поведение родителя, используется parent:::
class Dog extends Animal {
public function speak() {
$parent = parent::speak();
return $parent . ', но по-собачьи: гав';
}
}Результат: 'Бобик издаёт звук, но по-собачьи: гав'
Типичные ошибки:
- Забыть вызвать parent::__construct() - свойства родителя не инициализируются.
- Изменить сигнатуру метода (количество/типы параметров) - в PHP 8+ может вызвать фатальную ошибку при включённой строгой типизации.
- Попытка обратиться к приватным свойствам родителя напрямую - только через protected или публичные методы.
Как запретить переопределение метода?
Используется модификатор final перед объявлением метода. Пример:
class ParentClass {
final public function fixedMethod() { /* логика */ }
}
class ChildClass extends ParentClass {
public function fixedMethod() { /* ошибка */ }
}В результате - фатальная ошибка. Цель: гарантировать, что критическая логика не будет изменена.
Как запретить наследование класса?
Класс объявляется с final:
final class FinalClass { /* ... */ }
class SubClass extends FinalClass {} // Fatal errorПрименяется для классов, которые не должны расширяться (например, классы-синглтоны или конечные реализации).
Как заставить потомков реализовать определённый метод?
Создаётся абстрактный класс с абстрактным методом:
abstract class AbstractAnimal {
abstract public function makeSound(): string;
}
class Cat extends AbstractAnimal {
public function makeSound(): string { return 'Мяу'; }
}Невозможно создать экземпляр абстрактного класса. Цель: определить общий контракт для группы классов.
Как правильно наследовать конструктор?
Явный вызов parent::__construct() в конструкторе потомка:
class ParentClass {
public function __construct($value) { $this->value = $value; }
}
class ChildClass extends ParentClass {
public function __construct($value, $extra) {
parent::__construct($value);
$this->extra = $extra;
}
}Если не вызвать, значение $value не будет установлено. Цель: гарантировать инициализацию родительских свойств.
Как получить доступ к приватным данным родителя?
Приватные свойства видны только в классе, где определены. Решение: сделать свойство protected или предоставить публичный/защищённый метод доступа.
class ParentClass {
private $secret = 'секрет';
protected function getSecret() { return $this->secret; }
}
class ChildClass extends ParentClass {
public function reveal() { return $this->getSecret(); } // работает
public function fail() { return $this->secret; } // ошибка
}Цель: инкапсуляция - родитель контролирует доступ к своим данным.
Расширенные примеры наследования
Пример 1. Цепочка наследования и parent:: с несколькими уровнями
class Grandparent {
public function who() { return 'Дедушка'; }
}
class Parent extends Grandparent {
public function who() { return 'Родитель (наследует: ' . parent::who() . ')'; }
}
class Child extends Parent {
public function who() { return 'Ребёнок (наследует: ' . parent::who() . ')'; }
}
$obj = new Child();
echo $obj->who();Ребёнок (наследует: Родитель (наследует: Дедушка))
Каждый уровень добавляет свой контекст, используя parent::who() из предыдущего.
Пример 2. Абстрактный класс с общей логикой и абстрактными методами
abstract class Shape {
protected $color;
public function __construct($color) { $this->color = $color; }
abstract public function area(): float;
public function info(): string { return 'Цвет: ' . $this->color . ', площадь: ' . $this->area(); }
}
class Circle extends Shape {
private $radius;
public function __construct($color, $radius) {
parent::__construct($color);
$this->radius = $radius;
}
public function area(): float { return M_PI * $this->radius ** 2; }
}
class Rectangle extends Shape {
private $width, $height;
public function __construct($color, $w, $h) {
parent::__construct($color);
$this->width = $w; $this->height = $h;
}
public function area(): float { return $this->width * $this->height; }
}
$circle = new Circle('красный', 3);
$rect = new Rectangle('синий', 4, 5);
echo $circle->info() . PHP_EOL;
echo $rect->info() . PHP_EOL;Цвет: красный, площадь: 28.274333882308 Цвет: синий, площадь: 20
Абстрактный класс Shape содержит общее свойство color и метод info(), а area() оставляет наследникам.
Пример 3. Статические методы и позднее статическое связывание (static::)
Когда статический метод вызывается из наследника, self:: ссылается на класс, где метод определён, а static:: - на класс, из которого вызван.
class Ancestor {
public static function who() { echo 'Ancestor'; }
public static function testSelf() { self::who(); }
public static function testStatic() { static::who(); }
}
class Descendant extends Ancestor {
public static function who() { echo 'Descendant'; }
}
Descendant::testSelf();
Descendant::testStatic();AncestorDescendant
Полезно для реализации паттерна Template Method в статическом контексте.
Пример 4. Комбинация extends и implements
interface LoggerInterface {
public function log(string $msg): void;
}
class BaseLogger {
protected $prefix = '[LOG]';
}
class FileLogger extends BaseLogger implements LoggerInterface {
public function log(string $msg): void {
echo $this->prefix . ' ' . $msg . PHP_EOL;
}
}
$logger = new FileLogger();
$logger->log('Тестовое сообщение');[LOG] Тестовое сообщение
Наследование даёт структуру данных, интерфейс гарантирует контракт.
Пример 5. Конструкторы с разным количеством аргументов
class A {
public function __construct(...$args) {
echo 'A создан с: ' . implode(', ', $args) . PHP_EOL;
}
}
class B extends A {
public function __construct($x, $y) {
parent::__construct($x, $y);
echo 'B создан с: ' . $x . ', ' . $y . PHP_EOL;
}
}
$b = new B('first', 'second');A создан с: first, second B создан с: first, second