Event Bus и паттерн Observer в PHP: реализация и примеры

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Event Bus и паттерн Observer в PHP: от теории к практике

В современной разработке на PHP всё чаще требуется создавать слабосвязанные, гибкие и расширяемые системы. Два ключевых инструмента для достижения этой цели — паттерн Observer и его более продвинутая абстракция — Event Bus (Шина событий). В этой статье мы разберём, как реализовать оба подхода на PHP, и покажем, когда какой инструмент выбрать.

Что такое паттерн Observer?

Паттерн Observer (Наблюдатель) — это поведенческий шаблон проектирования, который определяет зависимость типа «один ко многим» между объектами. Когда один объект (издатель) меняет своё состояние, все зависимые объекты (подписчики) автоматически уведомляются об этом.

Простая реализация Observer в PHP:

<?php
interface Observer {
    public function update(string $event, $data): void;
}

interface Observable {
    public function attach(Observer $observer): void;
    public function detach(Observer $observer): void;
    public function notify(string $event, $data = null): void;
}

class UserManager implements Observable {
    private array $observers = [];

    public function attach(Observer $observer): void {
        $this->observers[] = $observer;
    }

    public function detach(Observer $observer): void {
        $this->observers = array_filter(
            $this->observers,
            fn($obs) => $obs !== $observer
        );
    }

    public function notify(string $event, $data = null): void {
        foreach ($this->observers as $observer) {
            $observer->update($event, $data);
        }
    }

    public function registerUser(string $name): void {
        // Логика регистрации
        echo "Пользователь $name зарегистрирован.\n";
        $this->notify('user.registered', ['name' => $name]);
    }
}

class EmailNotifier implements Observer {
    public function update(string $event, $data): void {
        if ($event === 'user.registered') {
            echo "Отправка email приветствия для {$data['name']}.\n";
        }
    }
}

class Logger implements Observer {
    public function update(string $event, $data): void {
        echo "Логирование события: $event\n";
    }
}

// Использование
$userManager = new UserManager();
$userManager->attach(new EmailNotifier());
$userManager->attach(new Logger());

$userManager->registerUser('Иван');
?>

Недостатки классического Observer

  • Жёсткая привязка — подписчики должны знать об издателе.
  • Отсутствие гибкости — сложно управлять множеством разнородных событий.
  • Проблемы с тестированием — каждый издатель содержит список наблюдателей.

Event Bus — эволюция паттерна Observer

Event Bus (Шина событий) — это централизованный посредник между издателями событий и их подписчиками. Вместо того чтобы каждый объект-издатель управлял списком наблюдателей, все подписки и уведомления проходят через единую шину.

Реализация Event Bus в PHP

<?php
class EventBus {
    private array $listeners = [];

    public function subscribe(string $event, callable $handler): void {
        $this->listeners[$event][] = $handler;
    }

    public function unsubscribe(string $event, callable $handler): void {
        if (!isset($this->listeners[$event])) {
            return;
        }
        $this->listeners[$event] = array_filter(
            $this->listeners[$event],
            fn($h) => $h !== $handler
        );
    }

    public function dispatch(string $event, $payload = null): void {
        if (!isset($this->listeners[$event])) {
            return;
        }
        foreach ($this->listeners[$event] as $handler) {
            call_user_func($handler, $payload);
        }
    }
}

// Пример использования
$bus = new EventBus();

// Подписка на события
$bus->subscribe('user.registered', function($data) {
    echo "Email: приветствуем {$data['name']}!\n";
});

$bus->subscribe('user.registered', function($data) {
    echo "Лог: пользователь {$data['name']} зарегистрирован.\n";
});

$bus->subscribe('user.deleted', function($data) {
    echo "Аудит: пользователь {$data['id']} удалён.\n";
});

// Издатель просто вызывает шину
function registerUser(EventBus $bus, string $name) {
    // Логика регистрации
    echo "Регистрируем $name...\n";
    $bus->dispatch('user.registered', ['name' => $name]);
}

function deleteUser(EventBus $bus, int $id) {
    echo "Удаляем пользователя $id...\n";
    $bus->dispatch('user.deleted', ['id' => $id]);
}

registerUser($bus, 'Мария');
deleteUser($bus, 42);
?>

Преимущества Event Bus перед Observer

Рекомендаций