Основы наследования
<?php
declare(strict_types=1);
class Animal
{
public function __construct(
protected string $name,
protected int $age,
) {
}
public function speak(): string
{
return "{$this->name} makes a sound";
}
public function getInfo(): string
{
return "{$this->name}, age {$this->age}";
}
}
class Dog extends Animal
{
public function __construct(
string $name,
int $age,
private string $breed,
) {
parent::__construct($name, $age); // Call parent constructor
}
// Override parent method
public function speak(): string
{
return "{$this->name} barks!";
}
public function getBreed(): string
{
return $this->breed;
}
}
$dog = new Dog('Rex', 5, 'Labrador');
echo $dog->speak(); // Rex barks!
echo $dog->getInfo(); // Rex, age 5 (inherited method)
echo $dog->getBreed(); // Labrador
Запомни: PHP поддерживает ТОЛЬКО одиночное наследование (один родительский класс). Для множественного "наследования" поведения используйте интерфейсы и трейты.
parent:: и self::
<?php
declare(strict_types=1);
class Base
{
public function greet(): string
{
return 'Hello from Base';
}
public static function create(): static
{
return new static(); // Late static binding
}
public function className(): string
{
return self::class; // Always Base
}
public function realClass(): string
{
return static::class; // Resolved at runtime
}
}
class Child extends Base
{
public function greet(): string
{
$parentGreet = parent::greet(); // Call parent version
return "{$parentGreet} and Child";
}
}
$child = new Child();
echo $child->greet(); // Hello from Base and Child
echo $child->className(); // Base (self:: resolves to defining class)
echo $child->realClass(); // Child (static:: resolves to calling class)
$obj = Child::create();
echo get_class($obj); // Child (late static binding with new static())
Ловушка экзамена:
self::classвсегда возвращает класс, где определён метод.static::classвозвращает класс, на котором метод вызван (позднее статическое связывание).new self()создаёт экземпляр определяющего класса,new static()— вызывающего.
Abstract классы и методы
<?php
declare(strict_types=1);
abstract class Shape
{
public function __construct(
protected string $color = 'red',
) {
}
// Abstract method — MUST be implemented by children
abstract public function area(): float;
abstract public function perimeter(): float;
// Concrete method — inherited as-is
public function describe(): string
{
return "A {$this->color} shape with area " . $this->area();
}
}
class Circle extends Shape
{
public function __construct(
private float $radius,
string $color = 'blue',
) {
parent::__construct($color);
}
public function area(): float
{
return M_PI * $this->radius ** 2;
}
public function perimeter(): float
{
return 2 * M_PI * $this->radius;
}
}
class Rectangle extends Shape
{
public function __construct(
private float $width,
private float $height,
string $color = 'green',
) {
parent::__construct($color);
}
public function area(): float
{
return $this->width * $this->height;
}
public function perimeter(): float
{
return 2 * ($this->width + $this->height);
}
}
// $shape = new Shape(); // Error! Cannot instantiate abstract class
$circle = new Circle(5.0);
echo $circle->area(); // 78.539...
echo $circle->describe(); // A blue shape with area 78.539...
Запомни: Абстрактный класс НЕ может быть создан напрямую (
new AbstractClass()= ошибка). Если класс содержит хотя бы один абстрактный метод, класс ОБЯЗАН быть объявлен какabstract. Дочерний класс ОБЯЗАН реализовать все абстрактные методы или сам быть abstract.
final
<?php
declare(strict_types=1);
// Final class — cannot be extended
final class Singleton
{
private static ?self $instance = null;
private function __construct() {}
public static function getInstance(): self
{
return self::$instance ??= new self();
}
}
// class MySingleton extends Singleton {} // Fatal error!
// Final method — cannot be overridden
class Payment
{
public function process(): void
{
$this->validate();
$this->execute();
}
// Children CANNOT override this method
final protected function validate(): void
{
if (!$this->isValid()) {
throw new InvalidArgumentException('Invalid payment');
}
}
protected function execute(): void
{
// Can be overridden
}
protected function isValid(): bool
{
return true;
}
}
class CreditCardPayment extends Payment
{
// This is OK:
protected function execute(): void
{
echo 'Processing credit card...';
}
// This would fail:
// protected function validate(): void {} // Cannot override final method!
}
Правила переопределения методов
<?php
declare(strict_types=1);
class Base
{
public function method(int $a, string $b = ''): string
{
return "$a-$b";
}
}
class Child extends Base
{
// Visibility can be WIDENED (not narrowed)
// public -> public (OK)
// protected -> public (OK — widening)
// public -> protected (ERROR — narrowing!)
// Parameter types can be CONTRAVARIANT (accept wider types)
// Return types can be COVARIANT (return narrower types)
// Compatible override (wider parameter, narrower return)
public function method(int|float $a, string $b = ''): string
{
return "$a:$b";
}
}
Ловушка экзамена: При переопределении видимость можно только РАСШИРИТЬ (protected -> public), но НЕЛЬЗЯ сужать (public -> protected). Типы параметров могут быть ШИРЕ (контравариантность), типы возврата — УЖЕ (ковариантность). Нарушение этих правил — Fatal Error.