Интерфейсы в PHP 8

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

 

Что такое интерфейс и зачем он нужен

Представьте, что вы пишете систему отправки уведомлений. Уведомления могут уходить по email, через SMS, в Telegram. Способы доставки разные, но у каждого есть общее: метод send(). Интерфейс позволяет закрепить этот «общий знаменатель» как обязательное требование — и писать код, который работает с любым типом уведомлений без привязки к конкретному классу.

 

Объявление и реализация интерфейса

Интерфейс объявляется ключевым словом interface. Методы внутри него не имеют тела — только сигнатуру. Класс принимает контракт через ключевое слово implements и обязан реализовать каждый метод интерфейса, иначе PHP выбросит фатальную ошибку.

interface Notifiable

{

    public function send(): void;

    public function getRecipient(): string;

}

 

class EmailNotification implements Notifiable

{

    public function __construct(

        private readonly string $email,

        private readonly string $message

    ) {}

 

    public function send(): void

    {

        echo "Email → {$this->email}: {$this->message}\n";

    }

 

    public function getRecipient(): string

    {

        return $this->email;

    }

}

 

class SmsNotification implements Notifiable

{

    public function __construct(

        private readonly string $phone,

        private readonly string $message

    ) {}

 

    public function send(): void

    {

        echo "SMS → {$this->phone}: {$this->message}\n";

    }

 

    public function getRecipient(): string

    {

        return $this->phone;

    }

}

Теперь напишем функцию, которая принимает любой объект, реализующий Notifiable, — не важно, Email это или SMS:

// Функция работает с интерфейсом, а не с конкретным классом

function dispatch(Notifiable $notification): void

{

    echo "Отправляем уведомление для: " . $notification->getRecipient() . "\n";

    $notification->send();

}

 

dispatch(new EmailNotification('user@mail.ru', 'Заказ принят'));

dispatch(new SmsNotification('+7 900 000 00 00', 'Ваш код: 1234'));

// Вывод:

// Отправляем уведомление для: user@mail.ru

// Email → user@mail.ru: Заказ принят

// Отправляем уведомление для: +7 900 000 00 00

// SMS → +7 900 000 00 00: Ваш код: 1234

Когда функция принимает тип Notifiable, а не EmailNotification, она становится независимой от конкретной реализации. Можно добавить TelegramNotification или PushNotification — функцию менять не придётся.

 

Множественная реализация интерфейсов

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

interface Loggable

{

    public function getLogMessage(): string;

}

interface Queueable

{

    public function getQueue(): string;

}

 

// Один класс — три интерфейса

class EmailNotification implements Notifiable, Loggable, Queueable

{

    public function __construct(

        private readonly string $email,

        private readonly string $message

    ) {}

 

    public function send(): void

    {

        echo "Email → {$this->email}\n";

    }

 

    public function getRecipient(): string

    {

        return $this->email;

    }

 

    public function getLogMessage(): string

    {

        return "[EMAIL] Отправлено на {$this->email}";

    }

 

    public function getQueue(): string

    {

        return 'notifications';

    }

}

 

$n = new EmailNotification('a@b.ru', 'Добро пожаловать!');

var_dump($n instanceof Notifiable); // true

var_dump($n instanceof Loggable);   // true

var_dump($n instanceof Queueable);  // true

 

Наследование интерфейсов

Интерфейс может расширять другой интерфейс через extends. Это позволяет строить иерархии контрактов: от общего к частному.

interface Sender

{

    public function send(): void;

}

// Расширяем базовый интерфейс — добавляем метод повтора

interface RetrySender extends Sender

{

    public function retry(int $attempts): void;

}

 

// Класс обязан реализовать оба метода: send() и retry()

class RobustEmailSender implements RetrySender

{

    public function send(): void

    {

        echo "Отправка письма...\n";

    }

 

    public function retry(int $attempts): void

    {

        echo "Повторная отправка, попытка: {$attempts}\n";

    }

}

 

$sender = new RobustEmailSender();

$sender->send();    // Отправка письма...

$sender->retry(2); // Повторная отправка, попытка: 2

var_dump($sender instanceof Sender);      // true

var_dump($sender instanceof RetrySender); // true

Интерфейс не может содержать свойства — только методы и константы. Попытка объявить свойство внутри интерфейса вызовет синтаксическую ошибку. Для свойств используйте абстрактные классы.

 

Интерфейс — это соглашение между частями программы. Он говорит: «Не важно, как именно ты работаешь внутри. Важно, что у тебя есть метод send() с вот такой сигнатурой». Это позволяет писать код, который легко расширяется: новый тип уведомлений, новый способ оплаты, новый источник данных — достаточно реализовать нужный интерфейс.

В PHP 8 интерфейсы стали ещё удобнее: типизация возвращаемых значений, тип mixed, never и объединённые типы расширяют выразительность контрактов. Интерфейсы, основа таких архитектурных принципов, как Dependency Inversion и принцип разделения интерфейсов из набора SOLID.

Лого

Spartacus_85 [Admin]

Администратор сайта — это специалист, который отвечает за техническую поддержку и бесперебойную работу веб-ресурса.



0 Комментарий(я)

Зарегистрируйтесь чтобы оставить комментарий