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

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

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

Начать курс

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

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

Паттерн Observer: основы

Паттерн Observer (Наблюдатель) — поведенческий шаблон проектирования, который создаёт механизм подписки, позволяющий одним объектам следить и реагировать на события, происходящие в других объектах. Основные компоненты:

  • Subject (Субъект) — объект, генерирующий события
  • Observer (Наблюдатель) — объект, реагирующий на события
  • Event (Событие) — информация о произошедшем действии

Event Bus: эволюция Observer

Event Bus — это централизованная шина, которая управляет всеми событиями в приложении. В отличие от классического Observer, где подписка жёстко привязана к субъекту, Event Bus предоставляет:

  • Слабое связывание отправителей и получателей
  • Возможность добавлять/удалять обработчики во время выполнения
  • Глобальный доступ к шине из любой части приложения
  • Упрощённое тестирование и отладку

Пример реализации Event Bus на PHP

Создадим простую, но функциональную реализацию Event Bus, совместимую с PSR-14 (Event Dispatcher).

<?php
// Интерфейс события
interface EventInterface
{
    public function getName(): string;
}

// Базовое событие
class UserRegisteredEvent implements EventInterface
{
    public function __construct(
        private string $email,
        private string $username
    ) {}

    public function getName(): string
    {
        return 'user.registered';
    }

    public function getEmail(): string
    {
        return $this->email;
    }

    public function getUsername(): string
    {
        return $this->username;
    }
}

// Интерфейс обработчика
interface EventHandlerInterface
{
    public function handle(EventInterface $event): void;
}

// Event Bus
class EventBus
{
    private array $listeners = [];

    public function addListener(string $eventName, EventHandlerInterface $handler): void
    {
        $this->listeners[$eventName][] = $handler;
    }

    public function removeListener(string $eventName, EventHandlerInterface $handler): void
    {
        if (!isset($this->listeners[$eventName])) {
            return;
        }

        $this->listeners[$eventName] = array_filter(
            $this->listeners[$eventName],
            fn($h) => $h !== $handler
        );
    }

    public function dispatch(EventInterface $event): void
    {
        $eventName = $event->getName();

        if (!isset($this->listeners[$eventName])) {
            return;
        }

        foreach ($this->listeners[$eventName] as $handler) {
            $handler->handle($event);
        }
    }
}
?>

Практическое использование

Теперь создадим обработчики и продемонстрируем работу шины:

<?php
// Обработчик отправки email
class SendWelcomeEmailHandler implements EventHandlerInterface
{
    public function handle(EventInterface $event): void
    {
        if (!$event instanceof UserRegisteredEvent) {
            return;
        }

        echo "Отправка приветственного письма на {$event->getEmail()}\n";
        // mail($event->getEmail(), 'Добро пожаловать!', '...');
    }
}

// Обработчик логирования
class LogUserRegistrationHandler implements EventHandlerInterface
{
    public function handle(EventInterface $event): void
    {
        if (!$event instanceof UserRegisteredEvent) {
            return;
        }

        echo "Логирование: пользователь {$event->getUsername()} зарегистрирован\n";
        // file_put_contents('logs.txt', "...", FILE_APPEND);
    }
}

// Настройка и использование
$bus = new EventBus();

$bus->addListener('user.registered', new SendWelcomeEmailHandler());
$bus->addListener('user.registered', new LogUserRegistrationHandler());

// Генерация события
$event = new UserRegisteredEvent('user@example.com', 'john_doe');
$bus->dispatch($event);

// Вывод:
// Отправка приветственного письма на user@example.com
// Логирование: пользователь john_doe зарегистрирован
?>

Расширенная версия с приоритетами

Добавим возможность задавать приоритет обработчиков:

<?php
class PriorityEventBus extends EventBus
{
    public function addListener(string $eventName, EventHandlerInterface $handler, int $priority = 0): void
    {
        $this->listeners[$eventName][] = [
            'handler' => $handler,
            'priority' => $priority
        ];

        // Сортировка по приоритету (выше = раньше)
        usort($                    

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