В предыдущей статье мы подробно разобрали **классы в PHP** — основу объектно-ориентированного программирования. Теперь перейдём к следующему важному понятию ООП: **интерфейсам**. Если класс — это «чертёж объекта», то интерфейс — это «договор», по которому класс обязуется реализовать определённое поведение.

Интерфейсы позволяют писать гибкий, расширяемый и тестируемый код. Они лежат в основе принципов SOLID, особенно **принципа инверсии зависимостей (DIP)** и **принципа разделения интерфейсов (ISP)**.

Что такое интерфейс?

**Интерфейс** в PHP — это абстрактный тип, который определяет **контракт**: набор методов, которые должен реализовать любой класс, его реализующий. Сам интерфейс **не содержит реализации** — только сигнатуры методов (имя, параметры, возвращаемое значение).

> 💡 Интерфейс отвечает на вопрос: «Что может делать объект?», но не «Как он это делает?».

Базовый синтаксис

Объявление интерфейса:
PHP:
interface Logger {
    public function log(string $message): void;
}

Реализация интерфейса классом:
PHP:
class FileLogger implements Logger {
    public function log(string $message): void {
        file_put_contents('app.log', $message . PHP_EOL, FILE_APPEND);
    }
}

- Ключевое слово `interface` — для объявления интерфейса.
- Ключевое слово `implements` — для подключения интерфейса к классу.
- Класс **обязан реализовать все методы**, объявленные в интерфейсе, с той же сигнатурой (включая типы параметров и возвращаемого значения).

## Зачем нужны интерфейсы? Основные преимущества

1. Гарантия совместимости

Если функция ожидает объект, реализующий интерфейс `Logger`, вы можете передать **любой** класс, реализующий этот интерфейс — `FileLogger`, `DatabaseLogger`, `EmailLogger` и т.д.

PHP:
function processUser(Logger $logger) {
    // ... какая-то логика ...
    $logger->log("Пользователь обработан");
}

$logger = new FileLogger();
processUser($logger); // Работает!

$logger = new DatabaseLogger();
processUser($logger); // Тоже работает!

Это называется **полиморфизмом** — один и тот же код работает с разными реализациями.

2. Лёгкое тестирование


Благодаря интерфейсам вы можете легко подменить реальные зависимости на **моки** (заглушки) при тестировании:
PHP:
class MockLogger implements Logger {
    public array $messages = [];

    public function log(string $message): void {
        $this->messages[] = $message;
    }
}

// В юнит-тесте
$mock = new MockLogger();
processUser($mock);
assert(in_array("Пользователь обработан", $mock->messages));

3. Слабая связанность (loose coupling)

Классы зависят не от конкретных реализаций, а от абстракций. Это упрощает изменение кода: вы можете заменить `FileLogger` на `CloudLogger`, и остальной код **не потребует изменений**, если оба реализуют один и тот же интерфейс.

Интерфейсы vs Абстрактные классы
Характеристика Интерфейс Абстрактный класс
Может содержать реализацию методовНет (до PHP 8.0); с PHP 8.0 — да, через trait или методы с телом (см. ниже)Да
Может содержать свойстваНетДа (включая protected/private)
Множественное наследованиеДа (implements A, B, C)Нет (extends только один
КонструкторНельзяМожно
Типичное использованиеОпределение поведения (что делает?)Общая логика (как делает?)

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


Класс может реализовывать **несколько** интерфейсов:
PHP:
interface Loggable {
    public function log(string $msg): void;
}

interface Notifiable {
    public function notify(string $msg): void;
}

class AdminService implements Loggable, Notifiable {
    public function log(string $msg): void {
        // запись в лог
    }

    public function notify(string $msg): void {
        // отправка уведомления
    }
}

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

Интерфейсы с методами по умолчанию (PHP 8.0+)

Начиная с **PHP 8.0**, в интерфейсах можно определять **методы с телом** (но только как `public` и без свойств):

PHP:
interface Renderable {
    public function render(): string;

    // Метод с реализацией по умолчанию
    public function display(): void {
        echo $this->render();
    }
}

class HtmlPage implements Renderable {
    public function render(): string {
        return "<h1>Привет, мир!</h1>";
    }
}

$page = new HtmlPage();
$page->display(); // Выведет: <h1>Привет, мир!</h1>

> ⚠️ Однако злоупотреблять этим не стоит — интерфейсы по-прежнему должны оставаться **контрактами**, а не местом для общей логики. Для повторного использования кода лучше использовать **трейты**.

Трейты vs Интерфейсы

- **Трейт (trait)** — механизм повторного использования кода («горизонтальное наследование»).
- **Интерфейс** — контракт поведения.

Часто они используются **вместе**:

PHP:
trait LoggableTrait {
    public function log(string $msg): void {
        error_log($msg);
    }
}

interface Loggable {
    public function log(string $msg): void;
}

class UserService implements Loggable {
    use LoggableTrait; // Реализация берётся из трейта
}

Такой подход сочетает **гибкость интерфейсов** и **удобство трейтов**.

Практический пример: система оплаты

Представим, что вы разрабатываете интернет-магазин с поддержкой разных платёжных систем.

PHP:
interface PaymentGateway {
    public function charge(float $amount): bool;
    public function refund(float $amount): bool;
}

class StripeGateway implements PaymentGateway {
    public function charge(float $amount): bool {
        // интеграция со Stripe API
        return true;
    }

    public function refund(float $amount): bool {
        // возврат через Stripe
        return true;
    }
}

class PayPalGateway implements PaymentGateway {
    public function charge(float $amount): bool {
        // интеграция с PayPal
        return true;
    }

    public function refund(float $amount): bool {
        // возврат через PayPal
        return true;
    }
}

class OrderService {
    public function __construct(
        private PaymentGateway $gateway
    ) {}

    public function completeOrder(float $total): void {
        if ($this->gateway->charge($total)) {
            echo "Заказ оплачен!";
        }
    }
}

// Гибкое использование
$order = new OrderService(new StripeGateway());
$order->completeOrder(99.99);

// Или
$order = new OrderService(new PayPalGateway());
$order->completeOrder(99.99);

Благодаря интерфейсу `PaymentGateway` вы легко можете:

- Добавлять новые платёжные системы.
- Писать автотесты с моками.
- Менять провайдеров без переписывания логики заказа.

Советы по проектированию интерфейсов

1. **Именуйте интерфейсы как прилагательные или существительные действия**:
`Loggable`, `Renderable`, `PaymentGateway`, `Authenticatable`.

2. **Следуйте принципу разделения интерфейсов (ISP)**:
Лучше создать несколько узкоспециализированных интерфейсов, чем один «божественный».

3. **Не добавляйте методы «на всякий случай»** — интерфейс должен отражать реальные потребности.

4. **Используйте скалярные типы и строгую типизацию** (PHP 7.0+), чтобы усилить контракт.

Заключение

Интерфейсы — это не просто синтаксическая конструкция PHP, а **инструмент проектирования**. Они помогают:

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

Освоив интерфейсы, вы сделаете важный шаг к профессиональному уровню в PHP-разработке. В следующей статье цикла мы рассмотрим **трейты (traits)** — ещё один мощный механизм повторного использования кода в PHP.

> ✅ **Помните**: интерфейсы — это не «дополнение», а основа современного, гибкого и поддерживаемого кода.

*Статья актуальна для PHP 8.3 (на момент написания: январь 2026 г.).*