Чат веб-разработчиков
Наследование в PHP

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

В этом уроке мы поговорим об ещё одном ките ООП – наследовании. Начнём урок, опять же, с аналогий наследования в реальной жизни.

Котики бывают двух полов: мальчики и девочки. Все мы прекрасно понимаем, чем они отличаются. Однако, у них есть и общие черты – независимо от пола и у тех и у тех есть четыре лапы, есть хвост, голова, усы и далее по списку. То есть есть что-то общее, а есть что-то, что их отличает. Так сказать, детали реализации.

В природе таких примеров уйма – это деление существ по классам и царствам, разделение по половому признаку, разделение по расовой принадлежности, и так далее. Любую вещь можно отнести к какому-то классу, а по каким-то другим признакам – отличить её от других вещей.

Так вот в программировании очень часто встречаются аналогичные ситуации, когда какой-то сущности (или нескольким сущностям) нужно повторить то же, что есть у другой сущности, но с какими-то дополнительными возможностями.

Предположим, на сайте есть простые записи в блоге. У них есть заголовок и текст. Администратор их может редактировать, добавлять что-то новое. А есть посты-уроки, как тот, что вы читаете прямо сейчас. И они должны иметь те же свойства и уметь делать всё то же самое, что и обычные посты, но вдобавок ко всему, у них должна быть домашка.

Давайте создадим для простых записей в блоге класс Post. Пусть, с некоторыми упрощениями, он выглядит так:

Код доступен только после покупки курса ООП в PHP.

Неужели, для того чтобы сделать класс урока с домашкой нам нужно копировать весь этот код в новый класс Lesson, а затем добавлять новое свойство homework и добавлять геттер и сеттер?

Оказывается – нет. Благодаря наследованию, разумеется. Как это работает? Да проще простого!

Класс может унаследовать от другого класса свойства и методы. Делается это при помощи ключевого слова extends (англ. - расширять). Вот так:

Код доступен только после покупки курса ООП в PHP.

Класс Lesson называют классом-наследником, или дочерним классом. Класс Post – родительский класс.
В качестве родительского класса при помощи слова extends можно указать только один класс. Однако, у класса Lesson, в свою очередь, тоже могут быть наследники. Они унаследуют все свойства и методы всех своих родителей.

При этом доступными внутри объектов класса-наследника будут только свойства или методы, объявленные в родительском классе как public или protected. Свойства и методы, с модификатором доступа private не будут унаследованы дочерними классами.

То есть, если мы хотим в классе Lesson объявить метод, который будет работать со свойствами title и text, то мы должны определить эти свойства не как private, а как protected:

Код доступен только после покупки курса ООП в PHP.

Теперь мы можем работать с ними и в классе Lesson:

Код доступен только после покупки курса ООП в PHP.

Вот таким нехитрым образом мы расширили класс Lesson, унаследовав его от класса Post. Как видим, мы избежали копирования кода, что не может не радовать.

При этом в объектах класса Lesson нам так же доступны все protected- и public-методы, объявленные в родительском классе. Давайте убедимся, в этом.

Код доступен только после покупки курса ООП в PHP.

Мы получим следующее:

Код доступен только после покупки курса ООП в PHP.

Как видим, всё прекрасно работает.

Модификаторы доступа

Давайте теперь вернёмся к модификаторам доступа и до конца проясним ситуацию, как каждый модификатор влияет на методы и свойства:

  • private – доступны только внутри объектов этого класса, недоступны в объектах классов-наследников;
  • protected – доступны внутри объектов этого класса и всем объектам классов-наследников. При этом недоступны извне;
  • public – доступны как внутри объектов класса, так и снаружи – можем напрямую обращаться к ним извне. Доступны объектам классов-наследников.

Все public-свойства и методы, то есть то, что позволяет нам напрямую взаимодействовать с объектами извне, называются интерфейсом класса.
Это, опять-таки, относится к инкапсуляции.

Вызов родительского метода

Если присмотреться к классам Post и Lesson, то можно заметить некоторое дублирование кода в конструкторе. Мы и там и там выполняем одинаковые действия для свойств title и text. Было бы неплохо от этого избавиться, воспользовавшись в Lesson уже готовым функционалом из Post.

Для этого мы можем вызвать родительский конструктор в конструкторе дочернего класса. Делается это при помощи ключевого слова parent и двойного двоеточия.

Код доступен только после покупки курса ООП в PHP.

Что именно произойдёт? В момент вызова конструктора класса Lesson (при создании нового объекта), сначала произойдёт вызов метода __construct из родительского класса, а затем задастся свойство homework. При этом этот метод из родительского класса отработает для свойств класса-наследника. Можно представить, что мы просто скопировали сюда содержимое этого метода из класса Post и вставили его сюда. Именно так и происходит, когда этот код выполняется.

Давайте проверим что всё работает:

Код доступен только после покупки курса ООП в PHP.

Результат:

Код доступен только после покупки курса ООП в PHP.

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

Как видим, тема наследования тесно связана с темой инкапсуляции. Одно без другого не работает. Уберёте одно – и другое сломается. На этом с наследованием всё, делайте домашку, а потом переходите к изучению полиморфизма.

Полный код, получившийся в ходе урока:

Код доступен только после покупки курса ООП в PHP.
Читайте также
Комментарии


SlipKnot
SlipKnot

Домашка:

class PaidLesson extends Lesson
{
    private $price;
    public function __construct(string $title, string $text, string $homework, float $price)
    {
        parent::__construct($text, $title, $homework);
        $this->price = $price;
    }
    public function getPrice()
    {
        return $this->price;
    }
    public function setPrice(float $price)
    {
        $this->price = $price;
    }
}

$paidLesson = new PaidLesson('Урок о наследовании PHP', 'лол, кек, чебурек',
    'Домашка: ложитесь спать, утро вечера мудренее', 99.90);
var_dump($paidLesson);

UPD: что такое "void"? Мы вроде такого не проходили. Или дайте ссылку где прочитать

ivashkevich
ivashkevich

Отлично!
А void - значит что функция ничего не возвращает.

Galay
Galay
class PaidLesson extends Lesson {
    private $price;
    public function __construct(string $title, string $text, string $homework, float $price) {
        parent:: __construct ($title, $text, $homework);
        $this->price = $price;
        }
    public function getPrice()
            {
                return $this->price;
            }
    public function setPrice(): void 
            {
                $this->price = $price;
            }
}
$pideLesson = new PaidLesson('Урок о наследовании в PHP','Лол, кек, чебурек','Ложитесь спать, утро вечера мудренее', 99.90);
var_dump($pideLesson);
g--nokoder
g--nokoder
Class PaidLesson extends Lesson{
    private $price;

    public function __construct($title, $text, $homework, $price){
        parent::__construct($title, $text, $homework);
        $this->price = $price;
    }

    public function setPrice($price):void {
        $this->price = $price;
    }
    public function getPrice(){
        return $this->price;
    }
}

$paidlesson = new PaidLesson("Урок о наследование в PHP", "лол, кек, чебурек", "Повторение мать учение!!!", "445,5 рубль");

echo "<br>";
echo "<pre>";
var_dump($paidlesson);
echo "</pre>";
ivashkevich
ivashkevich

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

g--nokoder
g--nokoder

Я писал типов аргументов, но PHPStorm выдает подсказку, это устарело и его использовали ниже версии 7. Так писать или нет?)

ivashkevich
ivashkevich

Не мог он такую подсказку дать, он говорит, что в Ваших настройках PHPStorm указана версия PHP < 7, а типы появились позже. Нужно зайти в настройки PHPStorm в пункт PHP и выбрать правильную версию.

g--nokoder
g--nokoder

Извиняюсь, английский не очень у меня, не правильно понял))

ivashkevich
ivashkevich

Ничего страшного, главное чтобы Вы на новой версии учились =)

computerix
computerix
<?php

class Post
{
    protected $title;
    protected $text;

    public function __construct(string $title, string $text)
    {
        $this->title = $title;
        $this->text = $text;
    }

    public function getTitle()
    {
        return $this->title;
    }

    public function setTitle($title): void
    {
        $this->title = $title;
    }

    public function getText()
    {
        return $this->text;
    }

    public function setText($text): void
    {
        $this->text = $text;
    }
}

class Lesson extends Post
{
    protected $homework;

    public function __construct(string $title, string $text, string $homework)
    {
        parent::__construct($title, $text);
        $this->homework = $homework;
    }

    public function getHomework(): string
    {
        return $this->homework;
    }

    public function setHomework(string $homework): void
    {
        $this->homework = $homework;
    }
}
class PaidLesson extends Lesson
{
    protected $price;

    public function __construct(string $title, string $text, string $homework, string $price)
    {
        parent::__construct($title, $text, $homework);
        $this->price = $price;
    }

    public function getPrice(): string
    {
        return $this->price;
    }

    public function setPrice(string $price): void
    {
        $this->price = =$price;
    }
}   
$lesson = new Lesson('Урок о наследовании в PHP', 'Лол, кек, чебурек', 'Ложитесь спать, утро вечера мудренее', '99.90');
var_dump($lesson);
DmitryGavrilov
DmitryGavrilov
class PaidLesson extends Lesson {

    private $price;

    public function __construct( string $title, string $text, string $homework, float $price)
    {
        parent::__construct($title, $text, $homework);
        $this->price = $price;
    }

    public function getPrice() {
        return $this->price;
    }

    public function setPrice(float $price) {
        $this->price = $price;
    }

}

$paidLesson  = new PaidLesson('Урок о наследование PHP', 'Лол,кек,чебурек',
    'Ложитесь спать, утро вечера мудренее', 99.90);
var_dump($paidLesson);
ArtemijeKA
ArtemijeKA
class PaidLesson extends Lesson
{
    private $price;

    public function __construct(string $title, string $text, string $homework, float $price)
    {
        parent::__construct($title, $text, $homework);
        $this->price = $price;
    }

    public function setPrice($price): void
    {
        $this->price = $price;
    }

    public function getPrice(): float
    {
        return $this->price;
    }
}

$paidLesson = new PaidLesson('Урок о наследовании в PHP', 'Лол, кек, чебурек', 'Ложитесь спать, утро вечера мудренее', 99.90);
var_dump($paidLesson);
$paidLesson->setPrice(55.234);
$paidLesson->setTitle('Новый title!');
$paidLesson->setText('Новый text!');
$paidLesson->setHomework('Новый homework!');
var_dump($paidLesson);

//object(PaidLesson)[1]
  //private 'price' => float 55.234
  //private 'homework' (Lesson) => string 'Новый homework!' (length=20)
  //protected 'title' => string 'Новый title!' (length=17)
  //protected 'text' => string 'Новый text!' (length=16)
ivashkevich
ivashkevich

Всё супер!

Benya
Benya
class PaidLesson extends Lesson
{
    private $price;

    public function __construct($title, $text, $homework, $price)
    {
        parent::__construct($title, $text, $homework);
        $this->price = $price;
    }

    public function getPrice(): float
    {
        return $this->price;
    }
    public function setPrice(float $price): void
    {
        $this->price = $price;
    }
}

$paid = new PaidLesson('Урок о наследовании в PHP', ' Лол, кек, чебурек', 'Ложитесь спать, утро вечера мудренее', '99.9');
var_dump($paid);
SBTesla
SBTesla
class PaidLesson extends Lesson {
     protected  $prise;

     public function __construct(string $title, string $text, string $homework, string $prise)
     {
          parent::__construct($title, $text, $homework);
          $this->prise = $prise;
     }

     public function getPrise():float {
          return $this->prise;
     }
     public function setPrise($prise): void
     {
          $this->prise = $prise;
     }
}

$PaidLesson = new PaidLesson('Урок о наследовании в PHP', 'Лол, кек, чебурек', 'Ложитесь спать, утро вечера мудренее','цена:99.90');
var_dump($PaidLesson);
ivashkevich
ivashkevich

Хорошо, только price через c.

AxLT
AxLT

Отличный курс, все доступно и понятно.

class PayLesson extends Lesson
{
    protected $price;

    public function __construct(string $title, string $text, string $homeWork, float $price)
    {
        parent::__construct($title, $text, $homeWork);
        $this->price = $price;
    }

    public function setPrice(float $price)
    {
        $this->price = $price;
    }

    public function getPrice(): float
    {
        return $this->price;
    }
}

$payLess = new PayLesson
(
    'Урок о наследовании в PHP',
    'Лол, кек, чебурек',
    'Ложитесь спать, утро вечера мудренее',
    99.90
);
var_dump($payLess);
ivashkevich
ivashkevich

Отлично! И спасибо =)