Зарегистрируйтесь, чтобы продолжить обучение

Интерфейсы PHP: Введение в ООП

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

Интерфейс в PHP — конструкция языка, описывающая абстрактный тип данных (АТД). Напомню, что АТД определяет набор операций (функций), независимых от конкретной реализации типа (в нашем случае класса) для манипулирования его значениями. На практике интерфейсы содержат определения функций (то есть описание их сигнатур) без реализации.

Хотя данная конструкция для нас в новинку, само понятие интерфейса используется на протяжении всего курса. В первую очередь это рассуждения о типах. Для оперирования точками на плоскости нам не нужна "реализация" точек. Достаточно того, что мы представляем их визуально и знаем операции, выполняемые над ними. То же самое касается и более базовых концепций, например, чисел и любых арифметических операций. Задумывались ли вы над тем, как на самом деле выполняются арифметические операции? Ответ на этот вопрос гораздо сложнее, чем может показаться на первый взгляд, и он зависит не только от языка, но и от конкретного аппаратного обеспечения (железа). Однако незнание ответа не мешает нам пользоваться числами, строками и массивами, не зная их устройства.

<?php

// file: DecartPointInterface.php

namespace App;

// Интерфейсы, по аналогии с классами, хранятся в своих собственных файлах
// и загружаются автоматически при следовании стандарту PSR-4.

// Имя интерфейса может быть любым, главное — соответствие PSR-4.
interface DecartPointInterface
{
    public function __construct($x, $y);
    public function getX();
    public function getY();
}

То, что раньше мы описывали словами и держали в голове, теперь явно записано в виде кода. Декартова точка — это АТД с тремя операциями:

  • Создание точки из двух значений
  • Извлечение координаты X
  • Извлечение координаты Y

По сути, прикладному коду больше ничего знать о точках и не нужно. Естественно, если нам понадобятся новые операции, то мы всегда можем их добавить, тем самым расширив интерфейс. Свои собственные АТД можно менять как угодно и когда угодно, только учтите, что изменение интерфейса обычно приводит к необходимости править код, использующий его.

Сама по себе конструкция Interface никак не влияет на остальной код. Недостаточно просто создать интерфейс, в этом нет смысла. Интерфейс должен быть реализован, и тогда он начнёт приносить пользу.

<?php

namespace AnotherApp;

// Импорт интерфейса
use App\DecartPointInterface;

class DecartPoint implements DecartPointInterface
{
    private $x;
    private $y;

    // Интерфейсные функции

    public function __construct($x, $y)
    {
        $this->x = $x;
        $this->y = $y;
    }

    public function getX()
    {
        return $this->x;
    }

    public function getY()
    {
        return $this->y;
    }

    // Не интерфейсные функции

    public function __toString()
    {
        return "({$this->getX()}, {$this->getY()})";
    }
}

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

Насколько наличие интерфейсов критично для PHP программ? Например, можно в любой программе открыть все файлы с классами и удалить часть определения класса, которая описывает реализацию интерфейсов (слово implements и то, что идёт за ним). После этого не изменится ровным счётом ничего — программа продолжит выполняться так же, как и выполнялась. Но ситуация меняется, если использовать интерфейс в сигнатурах функций и методов вместо классов.

<?php

function compare(DecartPointInterface $point1, DecartPointInterface $point2)
{
    // ...
}

Во время выполнения программы PHP проверяет, реализует ли класс соответствующий интерфейс, и если нет, то возникает ошибка. Причём проверка идёт именно на наличие записи implements в определении класса, а не на факт того, что методы определены (проверка реализации интерфейса гарантирует это).

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

Отдельно стоит сказать, что один класс может реализовывать любое число интерфейсов, в таком случае они описываются через запятую:

<?php

// Пример из библиотеки DS, внутри которой реализованы различные структуры данных в объектном синтаксисе
// https://github.com/php-ds/polyfill/blob/master/src/Stack.php
class Stack implements \IteratorAggregate, \ArrayAccess, Collection
{
    // some code
}

Здесь класс Stack реализует сразу три интерфейса. Это значит, что внутри него должны быть реализованы методы всех указанных интерфейсов.

Если у разных интерфейсов окажутся методы с одинаковым названием, но разной сигнатурой, то возникнет ошибка. В таком случае придётся отказаться от одного из интерфейсов либо переделать их (например, переименовать методы)

Интерфейс Countable

В PHP встроен интерфейс Countable, а функция count умеет работать с любым объектом, реализующим этот интерфейс.

<?php

class Collection implements Countable
{
    private $items;

    public function __construct($items = [])
    {
        $this->items = $items;
    }

    public function count()
    {
        return sizeof($this->items);
    }
}

$coll = new Collection([3, 2, 5]);
print_r(count($coll));
// => 3

https://repl.it/@hexlet/php-introduction-to-php-interfaces-countable


Дополнительные материалы

  1. Официальная документация
  2. Отделенный интерфейс
  3. Абстрактный тип данных

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 25 000 ₸ в месяц
Разработка веб-приложений на Laravel
10 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»