Object Inheritance

Оновлено: 11.05.2023

Спадкування - це добре відомий принцип програмування, і PHP використовує цей принцип у своїй об'єктній моделі. Цей принцип впливає на те, як багато класів та об'єктів пов'язані один з одним.

Наприклад, при розширенні класу підклас успадковує всі загальнодоступні та захищені методи, властивості та константи батьківського класу. Якщо клас не перевизначить ці методи, вони збережуть свою оригінальну функціональність.

Це корисно для визначення та абстрагування функціональності, а також дозволяє реалізувати додаткову функціональність в подібних об'єктах без необхідності повторно реалізовувати всю спільну функціональність.

Закриті методи батьківського класу є недоступними для дочірнього класу. Як наслідок, дочірні класи можуть самостійно перевизначати закриті методи, не зважаючи на звичайні правила успадкування. До версії PHP 8.0.0, однак, до приватних методів застосовувалися остаточні та статичні обмеження. Починаючи з версії PHP 8.0.0, єдиним обмеженням на приватні методи є приватні фінальні конструктори, оскільки це звичайний спосіб "відключити" конструктор, використовуючи замість нього статичні фабричні методи.

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

Зауважте:

Якщо не використовується автозавантаження, класи повинні бути визначені перед їх використанням. Якщо клас розширює інший, то батьківський клас повинен бути оголошений перед структурою дочірнього класу. Це правило застосовується до класів, які успадковують інші класи та інтерфейси.

Зауважте:

Не дозволяється перевизначати властивість для читання-запису властивістю тільки для читання і навпаки.

<?php

class A {
    public int $prop;
}
class B extends A {
    // Illegal: 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 is great.' . PHP_EOL;
    }
}

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

$foo = new Foo();
$bar = new Bar();
$foo->printItem('baz'); // Output: 'Foo: baz'
$foo->printPHP();       // Output: 'PHP is great' 
$bar->printItem('baz'); // Output: 'Bar: baz'
$bar->printPHP();       // Output: 'PHP is great'

?>

До версії PHP 8.1 більшість внутрішніх класів і методів не оголошували типи повернення, і при їх розширенні можна було використовувати будь-який тип повернення.

Починаючи з версії 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" as of 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" as of PHP 8.1.0
?>

Приклад #4 Перевизначальний метод оголошує неправильний тип повернення без повідомлення про застарілість

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