Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Интерфейсы (ключевое слово `interface`) 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. Абстрактный тип данных

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

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

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

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

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

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

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

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

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

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

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы

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

Иконка программы PHP-разработчик
Профессия
Разработка веб-приложений на Laravel
29 сентября 8 месяцев

Есть вопрос или хотите участвовать в обсуждении?

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

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг»