solophp/event-dispatcher

Minimal, PSR-14 compatible event dispatcher with priorities and stoppable propagation.

Maintainers

Package info

github.com/SoloPHP/Event-Dispatcher

pkg:composer/solophp/event-dispatcher

Statistics

Installs: 13

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.2.0 2026-03-06 21:24 UTC

This package is auto-updated.

Last update: 2026-03-06 21:26:47 UTC


README

Latest Version on Packagist License PHP Version Code Coverage

Minimal, PSR-14 compatible event dispatcher with priorities, stoppable propagation, and optional error logging.

Requirements

  • PHP 8.3+

Installation

composer require solophp/event-dispatcher

Usage

Adding Listeners Directly

use Solo\EventDispatcher\{EventDispatcher, ListenerProvider};

$provider = new ListenerProvider();

$provider->addListener(
    UserRegistered::class,
    fn (UserRegistered $e) => print "Welcome, {$e->username}!\n",
    priority: 10
);

$dispatcher = new EventDispatcher($provider);
$dispatcher->dispatch(new UserRegistered('john'));

Using Subscribers

use Solo\EventDispatcher\EventSubscriberInterface;

final class WelcomeEmailSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            UserRegistered::class => ['onUserRegistered', 10],
        ];
    }

    public function onUserRegistered(UserRegistered $event): void
    {
        echo "Welcome, {$event->username}!" . PHP_EOL;
    }
}

$provider = new ListenerProvider();
$provider->addSubscriber(new WelcomeEmailSubscriber());

$dispatcher = new EventDispatcher($provider);
$dispatcher->dispatch(new UserRegistered('john'));

Subscriber Configuration Formats

return [
    EventClass::class => 'methodName',
    EventClass::class => ['methodName', 10],                         // with priority
    EventClass::class => [['firstMethod', 20], ['secondMethod', 0]], // multiple handlers
];

Using the Factory

use Solo\EventDispatcher\EventDispatcherFactory;

$dispatcher = EventDispatcherFactory::create(
    listeners: [
        UserRegistered::class => fn (UserRegistered $e) => print "Welcome!\n",
    ],
    subscribers: [
        new WelcomeEmailSubscriber(),
        WelcomeEmailSubscriber::class,           // or class-string
        fn () => new WelcomeEmailSubscriber(),   // or factory callable
    ],
);

Error Logging

By default, if a listener throws an exception, it bubbles up to the caller. Pass a PSR-3 LoggerInterface to catch errors, log them, and continue executing the remaining listeners:

use Solo\EventDispatcher\{EventDispatcher, ListenerProvider};
use Psr\Log\LoggerInterface;

$dispatcher = new EventDispatcher($provider, $logger);

// or via factory
$dispatcher = EventDispatcherFactory::create(
    listeners: [...],
    subscribers: [...],
    logger: $logger,
);

When a listener fails, the following context is logged at error level:

Key Description
exception Exception class name
message Exception message
event Event class name
listener Listener description (e.g. Closure, MyClass::onEvent)
file File where the exception was thrown
line Line number

Stoppable Events

use Solo\EventDispatcher\AbstractStoppableEvent;

final class OrderPlaced extends AbstractStoppableEvent
{
    public function __construct(public int $orderId) {}
}

$provider->addListener(OrderPlaced::class, function (OrderPlaced $e) {
    if ($e->orderId < 0) {
        $e->stopPropagation(); // subsequent listeners won't be called
    }
}, priority: 100);

$provider->addListener(OrderPlaced::class, function (OrderPlaced $e) {
    // this won't run if propagation was stopped
});

Checking for Listeners

// checks for listeners including parent classes and interfaces
if ($provider->hasListenersFor(UserRegistered::class)) {
    $dispatcher->dispatch(new UserRegistered('john'));
}

Testing

composer test        # cs-check + analyze + phpunit
composer cs-check    # PHPCS (PSR-12)
composer cs-fix      # PHPCBF auto-fix
composer analyze     # PHPStan (level 8)

License

MIT