Наследование — это хорошо зарекомендовавший себя принцип программирования, и 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; } }
Here is some clarification about PHP inheritance – there is a lot of bad information on the net. PHP does support Multi-level inheritance. (I tested it using version 5.2.9). It does not support multiple inheritance.
This means that you cannot have one class extend 2 other classes (see the extends keyword). However, you can have one class extend another, which extends another, and so on.
Example:
<?php class A { // more code here }
class B extends A { // more code here }
class C extends B { // more code here }
$someObj = new A(); // no problems $someOtherObj = new B(); // no problems $lastObj = new C(); // still no problems
I think the best way for beginners to understand inheritance is through a real example so here is a simple example I can gave to you
<?php
class Person { public $name; protected $age; private $phone;
public function talk(){ //Do stuff here }
protected function walk(){ //Do stuff here }
private function swim(){ //Do stuff here } }
class Tom extends Person { /*Since Tom class extends Person class this means that class Tom is a child class and class person is the parent class and child class will inherit all public and protected members(properties and methods) from the parent class*/
/*So class Tom will have these properties and methods*/
//public $name; //protected $age; //public function talk(){} //protected function walk(){}
//but it will not inherit the private members //this is all what Object inheritance means }
You can force a class to be strictly an inheritable class by using the "abstract" keyword. When you define a class with abstract, any attempt to instantiate a separate instance of it will result in a fatal error. This is useful for situations like a base class where it would be inherited by multiple child classes yet you want to restrict the ability to instantiate it by itself.
Example........
<?php
abstract class Cheese { //can ONLY be inherited by another class }
class Cheddar extends Cheese { }
$dinner = new Cheese; //fatal error $lunch = new Cheddar; //works!
I was recently extending a PEAR class when I encountered a situation where I wanted to call a constructor two levels up the class hierarchy, ignoring the immediate parent. In such a case, you need to explicitly reference the class name using the :: operator.
Fortunately, just like using the 'parent' keyword PHP correctly recognizes that you are calling the function from a protected context inside the object's class hierarchy.
E.g:
<?php class foo { public function something() { echo __CLASS__; // foo var_dump($this); } }
class foo_bar extends foo { public function something() { echo __CLASS__; // foo_bar var_dump($this); } }
class foo_bar_baz extends foo_bar { public function something() { echo __CLASS__; // foo_bar_baz var_dump($this); }
public function call() { echo self::something(); // self echo parent::something(); // parent echo foo::something(); // grandparent } }
error_reporting(-1);
$obj = new foo_bar_baz(); $obj->call();
// Output similar to: // foo_bar_baz // object(foo_bar_baz)[1] // foo_bar // object(foo_bar_baz)[1] // foo // object(foo_bar_baz)[1]