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

Наследование — это хорошо зарекомендовавший себя принцип программирования, и PHP использует этот принцип в своей объектной модели. Этот принцип повлияет на то, как многие классы и объекты связаны друг с другом.

Например, при расширении класса дочерний класс наследует все общедоступные и защищённые методы, свойства и константы родительского класса. До тех пор пока эти методы не будут переопределены, они будут сохранять свою исходную функциональность.

Это полезно для определения и абстрагирования функциональности и позволяет реализовать дополнительную функциональность в похожих объектах без необходимости реализовывать всю общую функциональность.

Закрытые методы родительского класса недоступны для дочернего класса. В результате дочерние классы могут повторно реализовать закрытый метод без учёта обычных правил наследования. Однако до PHP 8.0.0 к закрытым методам применялись ограничения `final` и `static`. Начиная с PHP 8.0.0, единственное ограничение закрытого метода, которое применяется - это конструкторы `private final`, поскольку это обычный способ «отключить» конструктор при использовании вместо него статичных фабричных методов.

Видимость методов, свойств и констант можно ослабить, например, `защищённый` метод может быть помечен как `общедоступный`, но нельзя ограничить видимость, например, нельзя пометить `общедоступное` свойство как `закрытое`. Исключением являются конструкторы, видимость которых может быть ограничена, например, `общедоступный` конструктор может быть помечен как `закрытый` в дочернем классе.

> Замечание:

> Если не используется автозагрузка, классы должны быть объявлены до того, как они будут использоваться. Если класс расширяет другой, то родительский класс должен быть объявлен до наследующего класса. Это правило применяется к классам, которые наследуют другие классы или интерфейсы.

> Замечание:

> Не разрешается переопределять свойство чтения-записи с помощью readonly-свойства или наоборот.

>     <?php

>     class A
>     {
>         public int $prop;
>     }

>     class B extends A
>     {
>         // Нельзя: read-write -> readonly
>         public readonly int $prop;
>     }

>     ?>

Пример #1 Пример наследования

    <?php

    class Foo
    {
        public function printItem($string)
        {
            echo 'Foo: ' . $string . PHP_EOL;
        }

        public function printPHP()
        {
            echo 'PHP просто супер.' . PHP_EOL;
        }
    }

    class Bar extends Foo
    {
        public function printItem($string)
        {
            echo 'Bar: ' . $string . PHP_EOL;
        }
    }

    $foo = new Foo();
    $bar = new Bar();
    $foo->printItem('baz'); // Выведет: 'Foo: baz'
    $foo->printPHP();       // Выведет: 'PHP просто супер.'
    $bar->printItem('baz'); // Выведет: 'Bar: baz'
    $bar->printPHP();       // Выведет: 'PHP просто супер.'

    ?>

### Совместимость типов возвращаемых значений с внутренними классами

До PHP 8.1.0 большинство внутренних классов или методов не объявляли свои типы возвращаемых значений и при их расширении допускался любой тип возвращаемого значения.

Начиная с PHP 8.1.0, большинство внутренних методов начали "предварительно" объявлять тип возвращаемого значения. В этом случае тип возвращаемого значения методов должен быть совместим с расширяемым родителем; в противном случае выдаётся уведомление об устаревании. Обратите внимание, что отсутствие явного объявления типа возвращаемого значения также считается несоответствием сигнатуры и, соответственно, приводит к уведомлению об устаревании.

Если тип возвращаемого значения не может быть объявлен для переопределяемого метода из-за проблем с совместимостью с различными версиями PHP, может быть добавлен атрибут ReturnTypeWillChange, чтобы заглушить уведомление об устаревании.

Пример #2 Переопределяющий метод не объявляет никакого типа возвращаемого значения

    <?php
    class MyDateTime extends DateTime
    {
        public function modify(string $modifier) { return false; }
    }

    // "Deprecated: Return type of MyDateTime::modify(string $modifier) should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice", начиная с PHP 8.1.0
    ?>

Пример #3 Переопределяющий метод объявляет неверный тип возвращаемого значения

    <?php
    class MyDateTime extends DateTime
    {
        public function modify(string $modifier): ?DateTime { return null; }
    }

    // "Deprecated: Return type of MyDateTime::modify(string $modifier): ?DateTime should either be compatible with DateTime::modify(string $modifier): DateTime|false, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice", начиная с PHP 8.1.0
    ?>

Пример #4 Переопределяющий метод объявляет неверный тип возвращаемого значения без уведомления об устаревании

    <?php
    class MyDateTime extends DateTime
    {
        /**
         * @return DateTime|false
         */
        #[\ReturnTypeWillChange]
        public function modify(string $modifier) { return false; }
    }

    // Уведомление об устаревании не выводится
    ?>

`↑` and `↓` to navigate • `Enter` to select • `Esc` to close • `/` to open