ssnepenthe/wp-event-dispatcher

An event dispatcher abstraction over WordPress hooks

0.1.0 2023-11-19 23:04 UTC

This package is auto-updated.

Last update: 2024-05-20 00:17:17 UTC


README

A simple event dispatcher abstraction over WordPress hooks.

Warning

This package is currently in development and is subject to breaking changes without notice until v1.0 has been tagged.

It is one in a series of WordPress toys I have been working on with the intention of exploring ways to modernize the feel of working with WordPress.

As the label suggests, it should be treated as a toy.

Installation

composer require ssnepenthe/wp-event-dispatcher

Overview

Event dispatchers (WpEventDispatcher\EventDispatcherInterface) encourage the encapsulation of events into dedicated event classes and event listeners into dedicated subscriber classes.

Usage

The event dispatcher is intended to be used in place of direct calls to the various action and filter functions provided by WordPress.

Event dispatchers do away with the idea of actions and filters altogether. Events are wrapped up in standalone event classes. If you want to "filter" a value, do so using typed properties or setters and getters on the event class. Event listeners can be standalone callables, but should preferably be implemented via subscriber classes.

This is roughly modeled after the Symfony event dispatcher component, but uses WordPress hooks behind the scenes.

The primary methods you will use are dispatch, addListener, and addSubscriber.

By default dispatch uses the FQCN of the event class as the event name. If an event needs a dynamic name it should implement the WpEventDispatcher\NamedEventInterface interface.

Event dispatchers might require a bit extra boilerplate code as compared to standard WordPress hooks, but provide a degree of type-safety and autocompletion you wouldn't otherwise get.

Below is a simple example of how you might replace a WordPress action:

Before:

add_action('my_plugin_initialized', function () {
    // ...
});

do_action('my_plugin_initialized');

After:

class MyPluginInitialized
{
}

$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener(MyPluginInitialized::class, function (MyPluginInitialized $event) {
    // ...
});

$eventDispatcher->dispatch(new MyPluginInitialized());

And an example of how you might replace a filter:

Before:

add_filter('my_plugin_filtered_value', function ($value) {
    if (is_string($value)) {
        $value = modifyValue($value);
    }

    return $value;
});

$default = 'some string';
$value = apply_filters('my_plugin_filtered_value', $default);

if (! is_string($value)) {
    $value = $default;
}

After:

class MyPluginFilteredValue
{
    public function __construct(public string $value)
    {
    }
}

$eventDispatcher = new EventDispatcher();
$eventDispatcher->addListener(MyPluginFilteredValue::class, function (MyPluginFilteredValue $event) {
    $event->value = modifyValue($event->value);
});

$event = new MyPluginFilteredValue('some string');
$eventDispatcher->dispatch($event);
$value = $event->value;

Event dispatchers also allow for event listeners to be grouped logically within a subscriber class.

Subscribers should implement the WpEventDispatcher\SubscriberInterface interface.

This interface has a single method - getSubscribedEvents. It should return an array in any of the following formats:

Array keys are hook tag names, values are method names on this subscriber instance to use as listeners.

return [
    'the_content' => 'onTheContent',
];

Array keys are hook tag names, values are arrays with method names at index 0 and optional priority at index 1.

return [
    'the_content' => ['onTheContent', 20],
];

Array keys are hook tag names, values are arrays of array with method names at index 0 and optional priority at index 1.

return [
    'the_content' => [
        ['onTheContent', 20],
        ['alsoOnTheContent'],
    ],
];

For (a very contrived) example:

class PostContentSubscriber implements SubscriberInterface
{
    public function getSubscribedEvents(): array
    {
        return [
            'the_content' => [
                ['appendSomething'],
                ['prependAnotherThing', 20],
            ],
        ];
    }

    public function appendSomething($content)
    {
        return $content . ' something';
    }

    public function prependAnotherThing($content)
    {
        return 'another thing ' . $content;
    }
}

Initialize the subscriber with the addSubscriber method on the event dispatcher:

$eventDispatcher->addSubscriber(new PostContentSubscriber());