MidТеория3 min

Наследование

extends, parent::, final, abstract

Основы наследования

<?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.


Вопросы с экзамена ZCE

Проверь себя

5 из 19

Что выведет следующий код? ```php class MyEx1 extends Exception {} class MyEx2 extends MyEx1 {} function checkValue(float $a): void { if ($a < 10) throw new MyEx1('too small.'); } try { checkValue(50); checkValue(5); } catch (MyEx2 $e) { echo $e->getMessage(); } catch (Exception | MyEx1 $e) { echo get_class($e); } finally { echo 'End'; } ```

Что выведет следующий код? ```php abstract class myBaseClass { abstract protected function doSomething(); function threeDots() { return '...'; } } class myClassA extends myBaseClass { protected function doSomething() { echo $this->threeDots(); } } $a = new myClassA(); $a->doSomething(); ```

Что отобразится при выполнении следующего кода? ```php abstract class A { abstract public function f(); } (new anonymousclass extends A { public function f() { echo 'Hello World'; } })->f(); ```

Каковы основные преимущества замены фатальных ошибок на исключения в PHP 7?

Какое ключевое слово предотвращает переопределение метода/класса в подклассе?