Перейти к содержимому
Теория · Классы и объекты · урок 1 из 11

Классы и объекты

Чему вы научитесь

Чему вы научитесь:

  • описывать класс со свойствами и методами и создавать по нему объекты через new;
  • задавать начальные значения в конструкторе и закрывать свойства словом private;
  • объяснять, почему $b = $a не копирует объект, и копировать его через clone;
  • пользоваться статическими свойствами, константами класса, readonly и __toString.
Чертёж дома с надписью class и два построенных по нему дома с табличками

Представьте чертёж дома. На нём размечены комнаты, окна и двери, но жить в чертеже нельзя. По одному чертежу строят сколько угодно домов: устроены они одинаково, а мебель и жильцы в каждом свои.

В PHP чертёж называется классом, а построенный по нему дом — объектом. Эта картинка пройдёт через всю тему: чертёж, дома по нему и адреса этих домов.

Зачем нужен чертёж, если данные можно хранить в массиве? Например, товар в массиве: ['title' => 'tea', 'price' => 5]. Но у массива есть слабые места:

  • массив не знает, какие ключи у него должны быть, и опечатку в ключе PHP заметит только при чтении;
  • функции для работы с товаром живут отдельно от данных и примут любой массив.

Класс собирает в одном месте, какие данные есть у объекта и что с ним можно делать.

Класс и объект

Давайте опишем счётчик посещений страницы:

PHP
<?php

class Counter
{
    public int $value = 0;
}

$visits = new Counter();
$visits->value = $visits->value + 1;
echo $visits->value, "\n"; // 1

Разберём программу по шагам:

  1. class Counter объявляет класс с именем Counter. Его тело стоит в фигурных скобках, и открывающая скобка занимает отдельную строку.
  2. public int $value = 0; объявляет свойство $value типа int с начальным значением 0. Слово public значит, что свойство доступно из любого места программы.
  3. new Counter() строит объект по классу, и он попадает в переменную $visits.
  4. Стрелка -> открывает доступ к свойству этого объекта: $visits->value читается как «value объекта из $visits».
  5. Программа увеличивает счётчик на единицу и печатает 1.

Класс — описание, по которому строят объекты: какие у них данные и что с ними можно делать. Объект — значение, построенное по классу через new, его ещё называют экземпляром класса. Свойство — переменная внутри объекта; объявляют её в классе.

Имена классов пишут с заглавной буквы, и каждое следующее слово в имени тоже: Counter, ShoppingCart.

У свойств три правила:

  • тип пишут перед именем, как у параметра функции: int, string, array, ?string;
  • начальное значение получает каждый новый объект;
  • тип проверяется при каждой записи, как у параметра функции в режиме приведения: '5' станет 5, а $visits->value = 'many' закончится ошибкой TypeError.

Один new — один объект

Каждый new строит по тому же чертежу новый дом. Свойства у каждого объекта свои:

PHP
$home = new Counter();
$about = new Counter();
$home->value = 7;
echo $home->value, ' ', $about->value, "\n"; // 7 0

$home и $about — два разных объекта класса Counter. Число 7 попало только в свойство объекта из $home, а у объекта из $about осталось начальное 0.

Этот вопрос часто задают на собеседовании: «Чем класс отличается от объекта?» Ответ: класс — это описание, а объект — конкретный экземпляр, построенный по нему через new. Объектов одного класса может быть сколько угодно, и у каждого свои значения свойств.

Метод и $this

Класс описывает не только данные, но и действия с ними. Функцию, объявленную внутри класса, называют методом. Вот корзина покупок с методом add:

PHP
<?php

class Cart
{
    public array $items = [];

    public function add(string $item): void
    {
        $this->items[] = $item;
    }
}

$cart = new Cart();
$cart->add('tea');
echo implode(' ', $cart->items), "\n"; // tea

Давайте разберём:

  1. public function add(string $item): void объявляет метод. Он устроен как обычная функция, только живёт внутри класса.
  2. $cart->add('tea') вызывает метод на объекте из $cart через ту же стрелку ->.
  3. Внутри метода $this — объект, на котором метод вызвали. В этом вызове $this — тот же объект, что лежит в $cart.
  4. $this->items[] = $item; добавляет 'tea' в свойство items этого объекта, а echo implode(' ', $cart->items) печатает tea.

Здесь картинка с чертежом неточна. Метод не встроен в каждый дом: он описан в классе один раз. С каким домом работать, метод при каждом вызове узнаёт из $this.

Частая ошибка

Внутри метода к свойству обращаются без $this->:

PHP
<?php

class Cart
{
    public array $items = [];

    public function add(string $item): void
    {
        $items[] = $item; // ✗
    }
}

$cart = new Cart();
$cart->add('tea');
echo count($cart->items), "\n"; // 0

PHP не сообщает об ошибке, и программа печатает 0. Как вы уже знаете из темы о функциях, у тела функции своя область видимости, и у метода тоже. $items здесь — новая локальная переменная метода, а не свойство объекта: PHP создаёт её, кладёт в неё 'tea' и забывает после вызова.

К свойству объекта внутри метода всегда обращаются через $this: $this->items[] = $item;.

Главное

  • Класс — чертёж, а new Counter() строит по нему отдельный объект со своими свойствами.
  • Свойство объявляют в классе с типом и начальным значением, а читают и меняют через ->.
  • Метод — функция внутри класса, и $this в нём — объект, на котором метод вызвали.
  • Без $this-> имя внутри метода — локальная переменная, а не свойство.