34
loading...
This website collects cookies to deliver better user experience
class
object containing the context. Because a class
already has a name, this name widely adopted as the event name.Note: You can probably forget the terms: Emitter & Listener Provider. These are merely words for: your existing code that calls the dispatcher & the configuration that maps the listeners to the event. The latter will be different and be named differently in every framework.
callable
, either an anonymous function, a method on a specific class, or even an invokable class. This callable will receive the event object or value that was provided as its argument; or context. Sometimes the name of the event is enough information for the listener to act upon; but most of the time it will need the event object or value.Fact: Did you know that there is a list of recommendations for PHP on how to work with certain concepts? These are called PHP Standard Recommendations; or PSRs. One of these recommendations is PSR-14: Event Dispatcher. This recommendation explains in technical terms how event dispatching could work.
hook
(more on those later on). The rest of the arguments are available to the listener as context. These canstring
, bool
, int
or float
) or even an array
as a copy to the receiving function or callable. This means you cannot change the variable that is provided. Therefore, the listeners return
the new value back to the dispatcher.Note: While it is possible to change a value of a variable via a function if it is passed by reference; this is tricky business as are changing the value inside the variable. And that is not always what you want; e.g. when you use that same variable later on and expect it to still be the original value.
__return_true()
that will always return true
. These are handy to quickly change a setting via an event. A downside to this approach is that, as your context arguments grow, the listener callable should also receive them as arguments. I’ve been in situations where I (only) needed the 11th(!) parameter as context to change the first argument.// Somewhere in a plugin the `get_title` event is dispatched.
$post = get_post();
$title = 'Original title';
$title = apply_filters('get_title', $title, $post);
// Somewhere in a functions.php for example:
// Adding the post ID before the title.
add_filter('get_title', function (string $title, ?WP_Post $post): string {
return ($post ? $post->ID . ': ' : '') . $title;
}, 10, 2);
return
anything, but can interact with the object via the provided methods.// Somewhere you subscribe to the event.
$dispatcher->subscribeTo(PostCreatedEvent::class, function (PostCreatedEvent $event): void {
$post = $event->getPost();
$newTitle = sprintf('%d: %s', $post->getId(), $post->getTitle());
$post->setTitle($newTitle);
});
// Somewhere in the source code.
$post = $this->createPost('Original title');
$event = $dispatcher->dispatch(new PostCreatedEvent($post));
$title = $event->getPost()->getTitle(); // Will have the new title.
returns
the event instance after dispatching it to all the listeners.Side note: While it isn’t common practice in WordPress plugins or themes to use event objects, I think it really should be, as it makes for a more enjoyable developer experience. Just create an event object inside your plugin and dispatch that when it makes sense.
// WordPress will have a priority number. The lower the number, the earlier it will be called.
add_filter('the_event_name', '__return_true', 10);
// Symfony works the other way around. The higher the number, the more priority it will have.
$dispatcher->addListener(SomeEvent::class, [$listener, 'onBeforeSave'], 30); // 0 is the default.
// `thephpleage/event` package also prefers a higher number for earlier calls.
$dispatcher->subscribeTo(SomeEvent::class, [$listener, 'onBeforeSave'], PHP_INT_MAX); // Must be the first call.
function
that can be called to signal the dispatcher to stop any propagation of the event. The dispatcher has to listen to this call. It is not possible to cancel this action.// Symfony has a base `Event` class that contains a `stopPropagation()` method.
// The same holds true for the `thephpleage/event `package`, but there you extend `StubStoppableEvent`
// or implement `StoppableEventInterface` (PSR-14).
public function listener(SomeEvent $event): void {
// Do stuff here.
$event->stopPropagation();
}
// Laravel will stop propagation if the listener returns `false`.
public function listener(SomeEvent $event) {
// Do stuff here.
return false;
}
apply_filter()
(hook) and do_action()
(action). So what’s the difference? It all depends on whether your code needs the result.setTitle()
method to change it. The code will then use the value from the event.addDataSource()
method that a listener can use to add a data source. This means you can have multiple data sources, that only respond when they want to. So even if your importer runs every hour, the data source doesn't have to be included every hour.// Laravel style of event dispatching.
$event = DetermineDataSourcesEvent::dispatch();
foreach($event->getImporters() as $importer) {
$this->importFrom($importer);
}
Note: because a hook is used by the code before an action is triggered, it is common to name them in present tense: e.g. DetermineDataSourcesEvent
or BeforeSaveEvent
.
Note: because an action is dispatched after an action, it is common to name them in past tense: e.g. BlogPostCreatedEvent
or AfterSaveEvent
.
// Symfony style of dispatching an event.
use Symfony\Component\EventDispatcher\EventDispatcher;
$post = $this->createBlogPost(); // Some code that creates the blog post.
$dispatcher = new EventDispatcher();
$dispatcher->dispatch(new BlogPostCreatedEvent($post));
before
-events, and actions as after
-events.webhooks
and are wondering if those are just another term for hooks
. They are not. Though webhooks are events, they are events that are triggered via an HTTP request. Meaning the "listeners" for these events are (other) websites URLs. Therefore, the listener does not have to be a PHP script. It can be any webbased application. It just needs to understand the request.