Overview

Description

Caldera Events is a PSR-14 event dispatcher implementation.

As with the other Caldera components it has been built to be swappable and modular.

Installation

The easisest way to install it is to use Composer:

composer require vecode/caldera-events

Requires

  • php: >=8.1
  • ext-mbstring: *
  • psr/container: ^2.0
  • psr/event-dispatcher: ^1.0

Basic usage

Getting started

Events are a handy way of connecting components in an application so that they can react to certain actions. For example, sending an email when a new user account is created.

To that end, PSR-14 defines a common interface to create an extendable event dispatcher and this component offers a simple implementation.

Its usage is very straightforward, but it revolves around three components, the ListenerProvider, an EventDispatcher and an Event class.

The ListenerProvider implements ListenerProviderInterface and it is used to keep track of the listeners and then retrieve them for an specific Event; creating one is simple:

use Caldera\Events\ListenerProvider;

$listener_provider = new ListenerProvider();

Now, to start adding listeners you must define your event class. Event classes aren't bound to an specific interface, so you can have as many members/methods as you want, for example:

class TestEvent {

    protected string $value;

    function __construct(string $value) {
        $this->value = $value;
    }

    public function getValue(): string {
        return $this->value;
    }
}
Adding listeners

Once you've defined your Event class, you can add a listener to your ListenerProvider instance:

use Caldera\Events\ListenerProvider;

$listener_provider = new ListenerProvider();
$listener_provider->add(TestEvent::class, function(TestEvent $event) {
   // Do something with $event
});

A listener can be a simple Closure that receives the $event object; however internally they are bound using the CallableListener class, as all listeners must implement ListenerInterface.

You can also create your own ListenerInterface implementations:

class TestListener implements ListenerInterface {

    public function handle(object $event): void {
        // Do something with $event
    }
}

As you can see, the only difference is the $event type, as the Closure allows precise typing.

To use them just pass the class name:

use Caldera\Events\ListenerProvider;

$listener_provider = new ListenerProvider();
$listener_provider->add(TestEvent::class, TestListener::class);
Dispatching events

To dispatch events you will need an EventDispatcher; creating one is simple and you just need your ListenerProvider instance:

use Caldera\Events\ListenerProvider;
use Caldera\Events\EventDispatcher;

$listener_provider = new ListenerProvider();
$listener_provider->add(TestEvent::class, TestListener::class);

$dispatcher = new EventDispatcher($listener_provider);

These two objects (the ListenerProvider and EventDispatcher) are best kept inside a DI container, so we recommend using a PSR-11 implementation for this, as you will need to resolve your EventDispatcher from the classes that trigger the events.

For example, to dispatch a TestEvent you can do:

use Caldera\Events\EventDispatcher;

$dispatcher = resolve(EventDispatcher::class); // where resolve uses your DI container to resolve the service instance
$event = new TestEvent('foo');
$dispatcher->dispatch($event);

Or better yet, with dependency injection:

use Caldera\Events\EventDispatcher;

class ClassThatDispatchesTestEvent {

    protected EventDispatcher $dispatcher;

    public function __construct(EventDispatcher $dispatcher) {
        $this->dispatcher = $dispatcher;
    }

    public function doSomething(string $value) {
        $event = new TestEvent($value);
        $this->dispatcher->dispatch($event);
    }
}

That way the dependency is clear and handled automatically by the container, just make sure your container implementation supports dependency injection.

You can also create your own specialized EventDispatcher and/or ListenerProvider classes to handle specific events or provide specific listeners; just extend these classes or implement directly the interfaces.

Stoppable events

For example, the PSR-14 defines an special event type that can be stopped through the StoppableEventInterface which StoppableEvent implements.

To use it your events must extend that class:

use Caldera\Events\Event\StoppableEvent;

class TestEvent extends StoppableEvent {
    // Event logic
}

And in your listeners just call the stopPropagation method to stop any further propagation:

$listener_provider->add(TestEvent::class, function(TestEvent $event) {
    $event->stopPropagation();
});

Remember that you can always create your own event types with custom logic, for example, one-time events or anything that you may need for your particular application.