# Designing a system: WordPress event management

In a [previous article](https://carlalexander.ca/designing-classes-wordpress-plugin-api/), we went over the concept of events and event listeners. These were classes who were in charge of a specific [aspect](https://en.wikipedia.org/wiki/Aspect_(computer_programming)) of an application. This was a more abstract job that touched several parts of an application. We also saw how you could design these event listeners in WordPress. It required that you rethink how you use the [plugin API](https://carlalexander.ca/wordpress-adventurous-plugin-api/). (Yes, this is going to be another plugin API article :P) We're going to keep going with this idea of events and event listeners. We're going to design a system for them. We'll call it an "event management" system. It's an important tool in your journey to master [object-oriented programming in WordPress](http://carlalexander.ca/object-oriented-programming-next-step-wordpress-developer/).

# On actions and filters

Before we begin, let's do a small recap on the plugin API language. When we talk about the plugin API, we often talk about action hooks and filter hooks. Actions are is a type of signal that the plugin API emits. Anyone can listen for those signals and react to them. Filters are a bit different. They're also a type of signal that the plugin API emits. But that signal also contains data that you can change and send back. Now while those two types of signals do different things, they're the same under the hood. They use the same system and much of the same code. That's why this system won't distinguish between the two of them. We're going to simplify the language of the plugin API by replacing them by a single term: hook. This also means that we won't always have methods for both types of signals. In most cases, having methods for each is redundant. Using the same term for both lets us remove some of that redundancy. # Why do we need this?

When you try to create classes for WordPress, the plugin API is always hanging over your head. You need it to interact with a lot of WordPress systems in a safe way, but it's always messy to design around it. That's why it's a common topic here. It's also too much to build a system to manage this when you're starting off. Instead, you focus on fixing the problem while designing each of your classes. Each class has its own methods and logic to connect to it. But this creates a new problem. All your classes end up coupled to the plugin API. If you need to make a change to how all your classes connect to it, you need to change them all. This is why we need this system. We need to extract this messy logic out into classes and interfaces. This leaves your classes free to only worry about the hooks they want to connect to. The system will take care of the rest. # The responsibilities of an event management system

Now that we've justified the need for the event management system. Let's look at how we'll break it down the responsibilities of the system. This is always a good exercise to do so that we ensure that each class focuses on a [single responsibility](https://carlalexander.ca/single-responsibility-principle-wordpress/). ## Interact with the plugin API

The most important job of the event management system is to interact with the plugin API. As we discussed earlier, having each class do it is messy and problematic. To fix this, we need a point of contact between our classes and the plugin API. This will let us extract all the calls to the plugin API out of our classes. ## Listen for events

That said, we still need classes to act as event listeners. Removing all the calls to the plugin API doesn't remove the need for this responsibility. It's still part of our event management system. ## Manage event listeners

We also need a class to manage our event listeners. We want to be able to add and remove listeners. When you add a listener, it'll register all its hooks with the plugin API. When you remove it, it'll remove them all. ## The big picture

Below is a graph showing all the components of the event management system. You have the event listeners who deal with the event manager when they want to talk with the plugin API. The event manager is the only one allowed to talk with the plugin API. The plugin API then dispatches a signal to the right event listeners when an event occurs and the cycle repeats itself. ![Event Management Graph](https://carlalexander.ca/wp-content/uploads/2016/01/Event-Management-Graph.png)# Coding our event management system

Now that we've broken down the responsibilities of our system, let's proceed to the next step. We need to transform these responsibilities into working code. ## PluginAPIManager class

The first thing that we'll do is create a class to manage the plugin API. It'll be our point of contact that we described earlier. We'll call it `PluginAPIManager` to reflect the job that it does. ```
class PluginAPIManager
{
    /**
     * Adds a callback to a specific hook of the WordPress plugin API.
     *
     * @uses add_filter()
     *
     * @param string   $hook_name
     * @param callable $callback
     * @param int      $priority
     * @param int      $accepted_args
     */
    public function add_callback($hook_name, $callback, $priority = 10, $accepted_args = 1)
    {
        add_filter($hook_name, $callback, $priority, $accepted_args);
    }

    /**
     * Executes all the callbacks registered with the given hook.
     *
     * @uses do_action()
     *
     * @param string $hook_name
     */
    public function execute()
    {
        $args = func_get_args();
        return call_user_func_array('do_action', $args);
    }

    /**
     * Filters the given value by applying all the changes from the callbacks
     * registered with the given hook. Returns the filtered value.
     *
     * @uses apply_filters()
     *
     * @param string $hook_name
     * @param mixed  $value
     *
     * @return mixed
     */
    public function filter()
    {
        $args = func_get_args();
        return call_user_func_array('apply_filters', $args);
    }

    /**
     * Get the name of the hook that WordPress plugin API is executing. Returns
     * false if it isn't executing a hook.
     *
     * @uses current_filter()
     *
     * @return string|bool
     */
    public function get_current_hook()
    {
        return current_filter();
    }

    /**
     * Checks the WordPress plugin API to see if the given hook has
     * the given callback. The priority of the callback will be returned
     * or false. If no callback is given will return true or false if
     * there's any callbacks registered to the hook.
     *
     * @uses has_filter()
     *
     * @param string $hook_name
     * @param mixed  $callback
     *
     * @return bool|int
     */
    public function has_callback($hook_name, $callback = false)
    {
        return has_filter($hook_name, $callback);
    }

    /**
     * Removes the given callback from the given hook. The WordPress plugin API only
     * removes the hook if the callback and priority match a registered hook.
     *
     * @uses remove_filter()
     *
     * @param string   $hook_name
     * @param callable $callback
     * @param int      $priority
     *
     * @return bool
     */
    public function remove_callback($hook_name, $callback, $priority = 10)
    {
        return remove_filter($hook_name, $callback, $priority);
    }
}
```

If you remove all the documentation, our `PluginAPIManager` class is quite small. That's normal since it's just a wrapper around the plugin API. That said, there are a few changes worth pointing out. ### Using a descriptive language

The change that'll stand out the most is the language used throughout the class. The documentation, method names and variables names all [share the same language](https://carlalexander.ca/importance-naming-programming/). The goal is to better define what each method does to the plugin API. Gone are words like action or filter. Like we mentioned earlier, actions and filters use the same underlying code. In fact, most action functions are just wrappers around the same filter function. That's why we introduced the term "hook" earlier. To the plugin API, an action or a filter is just a hook. But naming a method `add_hook` doesn't describe what it's doing that well either. We're not adding a hook to the plugin API. We're adding a [callback](https://secure.php.net/manual/en/language.types.callable.php) which is a function that the plugin API can call at a later time. This callback gets added to the `wp_filter` array. This an array that the plugin API uses as storage. The graph below shows you how the plugin API stores each time you call `add_filter` or `add_action`. ![wp_filter array](https://carlalexander.ca/wp-content/uploads/2015/08/wp_filter-array.png)You'll notice that there are quite a few elements to it. What matters to us is what's on the right. We're storing a callback (function on the graph) and the number of accepted arguments. And that's what we're changing most of the time when we interact with the plugin API. That's why most of the method names use the term "callback". ### Pushing things further

But there are two methods where pushed this new descriptive language to its limits. Those are the `execute` and `filter` method. These replace the `apply_filters` and `do_action` functions. Why not just call these methods `apply_callbacks` and `do_callbacks` and move on? Because `apply_callbacks` and `do_callbacks` aren't good method names either. They also don't describe what the plugin API is doing that well. This gives us an opportunity to create a clearer language and improve that. So why call it `execute` and not `do`? First, we can't use `do` as a method name. That's because `do` is a reserved term in PHP. You need it to define "[do-while](https://secure.php.net/manual/en/control-structures.do.while.php)" loops. Second, `execute` describes what the plugin API does when you call `do_action`. It reuses the same term used in the documentation. It says, "Execute functions hooked on a specific action hook." `filter` has a similar origin as `execute`. It also comes from the `apply_filters` documentation. That said, its documentation isn't as clear as the one for `do_action`. But, if you read through it, there's this description of the returned value at the end. It says, "The filtered value after all hooked functions are applied to it." This is a great description of what the plugin API is doing when you call `apply_filters`. The plugin API is filtering a value by applying changes to it. It's not applying hooks to a value. So that's the reasoning behind the `filter` name. ### More on the execute and filter methods

You might also have noticed that the `execute` and `filter` methods are a bit different from the rest. They don't make a direct call to `apply_filters` or `do_action`. Instead, they use [`call_user_func_array`](https://secure.php.net/manual/en/function.call-user-func-array.php) to call their respective function. We have to do that because you can pass as many arguments as you want to `apply_filters` and `do_action`. This makes it impossible to use method parameters like you would in normal circumstances. Instead, we have to use [`func_get_args`](https://secure.php.net/manual/en/function.func-get-args.php) to get all the arguments passed to our methods. We then pass them all to `call_user_func_array` as the second argument. To support PHP 5.2, we h ave to do this in two steps. That version of PHP won't let you call `func_get_args` insides `call_user_func_array`. To workaround that problem, we assign arguments returned by `func_get_args` to the `args` variable. We then pass `args` as the second argument of `call_user_func_array`. If you don't need to support PHP 5.2, you don't have to do it in two lines like we did in the code sample. ## Subscriber interface

The next step is to create a contract for our event listeners classes. It'll let them define which hooks they want to listen to. Let's take a look at it. ```
interface SubscriberInterface
{
    /**
     * Returns an array of hooks that this subscriber wants to register with
     * the WordPress plugin API.
     *
     * The array key is the name of the hook. The value can be:
     *
     *  * The method name
     *  * An array with the method name and priority
     *  * An array with the method name, priority and number of accepted arguments
     *
     * For instance:
     *
     *  * array('hook_name' => 'method_name')
     *  * array('hook_name' => array('method_name', $priority))
     *  * array('hook_name' => array('method_name', $priority, $accepted_args))
     *
     * @return array
     */
    public static function get_subscribed_hooks();
}
```

Again, this isn't a large piece of code. With interfaces, it's often all in the documentation (so always write good documentation!). It's where we explain the interface contract. In case you were curious, this contract is almost identical to [the one from Symfony](https://github.com/symfony/event-dispatcher/blob/v3.0.0/EventSubscriberInterface.php). ### So what does contract say?

The `SubscriberInterface` contract revolves around a single [static method](https://secure.php.net//manual/en/language.oop5.static.php) called `get_subscribed_hooks`. The contract requires that the method return an array. The array itself contains all the callbacks that the class wants to register with the plugin API. It must also follow a specific format to handle all the possible registration scenarios. These are: - Registering a class method to a specific hook.
- Registering a class method to a specific hook, but with a different priority.
- Registering a class method to a specific hook, but with a different priority and a different number of accepted arguments.

### Why SubscriberInterface?

So far, we've only used the term "event listener" throughout this article. Yet we're calling our interface `SubscriberInterface`. Why not call it `EventListenerInterface`? Let's take a moment to explain the name choice. The name comes from [Symfony](http://symfony.com/) and [Doctrine](http://www.doctrine-project.org/). Both libraries make the distinction between an "event listener" and an "event subscriber". To them, an event listener only listens to a single event. Meanwhile, a subscriber listens to several events. This changes how you look at the array returned by `get_subscribed_hooks`. It's not just an array with all the callbacks that our class wants to register to. It's an array of event listeners that our class wants to have registered. ## EventManager class

We have one final piece of class design left for our event management system. We need to connect our `SubscriberInterface` to our `PluginAPIManager` class. We'll explore three ways that we can achieve that. ### Standalone EventManager

The easiest way to solve our problem is by modifying our existing `PluginAPIManager` class. We'll just add new methods to it so that it can handle our `SubscriberInterface`. That said, to reflect its new job, we'll rename it from `PluginAPIManager` to `EventManager`. ```
class EventManager
{
    /**
     * Contains all the code from PluginAPIManager
     */
}
```

#### Adding an event subscriber

```
class EventManager
{
    // ...

    /**
     * Add an event subscriber.
     *
     * The event manager registers all the hooks that the given subscriber 
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->add_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Adds the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function add_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->add_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->add_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10, isset($parameters[2]) ? $parameters[2] : 1);
        }
    }
}
```

`add_subscriber` and `add_subscriber_callback` are the two methods that let you add an event subscriber. `add_subscriber` is the public method that others will interact with. It loops through all the callbacks returned by `get_subscribed_hooks`. It passes each callback to `add_subscriber_callback`. `add_subscriber_callback` does most of the work for registering a callback for a subscriber. It's a private method that takes in three parameters: `subscriber`, `hook_name` and `parameters`. `subscriber` is our `SubscriberInterface` object. `hook_name` is the name of the hook that `subscriber` wants to register to. `parameters` contains the callback registration details. All the logic in `add_subscriber_callback` revolves around the `parameters` variable. We need to determine which type of registration scenario it contains. Based on the contract that we wrote, it can be one of two things: an array or a string. It's a string containing the method name if we're dealing with the first scenario. Otherwise, it's an array for the other two scenarios. We start by checking if it's a string using [`is_string`](https://secure.php.net/manual/en/function.is-string.php). If it is, we pass it to our `add_callback` method. The first argument that we pass it is the name of the hook stored in `hook_name`. The second is a callable array. It contains our `SubscriberInterface` object and the `parameters` variable which stores our method name. If we don't have a string, we then use [`is_array`](https://secure.php.net/manual/en/function.is-array.php) to check if `parameters` is an array. If it's an array, we also want to verify that there's a value stored at index 0. That's because, per our interface contract, the method name always needs to be at index 0. The next line passes the data stored in the `parameters` array to our `add_hook` method. The first argument is still the `hook_name` variable. The second variable is also a callable array. We just replaced `parameters` with `parameters[0]` since that's where the method name is now. Things get a bit more complex here. Besides the method name at index 0, we're not sure what else is inside the `parameters` array. Is it just a priority? Or is it a priority and a number of accepted arguments? To figure that out, we have to use [ternary operators](https://secure.php.net/manual/en/language.operators.comparison.php#language.operators.comparison.ternary) to pass the last two arguments. That way, we can pass default values if the `parameters` array doesn't have anything. The default values that we use are the same as our `add_callback` method. #### Removing an event subscriber

```
class EventManager
{
    // ...

    /**
     * Remove an event subscriber.
     *
     * The event manager removes all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function remove_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->remove_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Removes the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function remove_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->remove_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->remove_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10);
        }
    }
}
```

Next, we'll look at how to remove an event subscriber. The code is almost the same as the one used to add an event subscriber. We also use two methods: `remove_subscriber` and `remove_subscriber_callback`. They both have the same parameters as `add_subscriber` and `add_subscriber_callback`. The only difference is in the `remove_subscriber_callback` method. It calls our `remove_callback` method instead of our `add_callback` method. #### Complete EventManager class

Before we move on, you might want a copy of the code for this version of the `EventManager` class. You can find it below. ```
class EventManager
{
    /**
     * Adds a callback to a specific hook of the WordPress plugin API.
     *
     * @uses add_filter()
     *
     * @param string   $hook_name
     * @param callable $callback
     * @param int      $priority
     * @param int      $accepted_args
     */
    public function add_callback($hook_name, $callback, $priority = 10, $accepted_args = 1)
    {
        add_filter($hook_name, $callback, $priority, $accepted_args);
    }

    /**
     * Add an event subscriber.
     *
     * The event manager registers all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->add_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Executes all the callbacks registered with the given hook.
     *
     * @uses do_action()
     *
     * @param string $hook_name
     */
    public function execute()
    {
        $args = func_get_args();
        return call_user_func_array('do_action', $args);
    }

    /**
     * Filters the given value by applying all the changes from the callbacks
     * registered with the given hook. Returns the filtered value.
     *
     * @uses apply_filters()
     *
     * @param string $hook_name
     * @param mixed  $value
     *
     * @return mixed
     */
    public function filter()
    {
        $args = func_get_args();
        return call_user_func_array('apply_filters', $args);
    }

    /**
     * Get the name of the hook that WordPress plugin API is executing. Returns
     * false if it isn't executing a hook.
     *
     * @uses current_filter()
     *
     * @return string|bool
     */
    public function get_current_hook()
    {
        return current_filter();
    }

    /**
     * Checks the WordPress plugin API to see if the given hook has
     * the given callback. The priority of the callback will be returned
     * or false. If no callback is given will return true or false if
     * there's any callbacks registered to the hook.
     *
     * @uses has_filter()
     *
     * @param string $hook_name
     * @param mixed  $callback
     *
     * @return bool|int
     */
    public function has_callback($hook_name, $callback = false)
    {
        return has_filter($hook_name, $callback);
    }

    /**
     * Removes the given callback from the given hook. The WordPress plugin API only
     * removes the hook if the callback and priority match a registered hook.
     *
     * @uses remove_filter()
     *
     * @param string   $hook_name
     * @param callable $callback
     * @param int      $priority
     *
     * @return bool
     */
    public function remove_callback($hook_name, $callback, $priority = 10)
    {
        return remove_filter($hook_name, $callback, $priority);
    }

    /**
     * Remove an event subscriber.
     *
     * The event manager removes all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function remove_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->remove_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Adds the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function add_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->add_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->add_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10, isset($parameters[2]) ? $parameters[2] : 1);
        }
    }

    /**
     * Removes the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function remove_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->remove_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->remove_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10);
        }
    }
}
```

### Extending PluginAPIManager

Now, we're going to complicate things a bit. Instead of adding our code to our `PluginAPIManager` class, we're going to extend it. The reason to do this has more to do with design philosophy than anything else. We want to acknowledge that `PluginAPIManager` is a valid class with its own job. And that its job might be different from `EventManager`. ```
class EventManager extends PluginAPIManager
{
    /**
     * Add an event subscriber.
     *
     * The event manager registers all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->add_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Remove an event subscriber.
     *
     * The event manager removes all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function remove_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->remove_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    /**
     * Adds the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function add_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->add_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->add_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10, isset($parameters[2]) ? $parameters[2] : 1);
        }
    }

    /**
     * Removes the given subscriber's callback to a specific hook
     * of the WordPress plugin API.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $hook_name
     * @param mixed               $parameters
     */
    private function remove_subscriber_callback(SubscriberInterface $subscriber, $hook_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->remove_callback($hook_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->remove_callback($hook_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10);
        }
    }
}
```

As you can see, this version of `EventManager` has the same four methods as our previous version. Their code hasn't changed at all either. What's changed is our ability to reuse `PluginAPIManager`. You can now use `PluginAPIManager` in other scenarios where you might not care about the `SubscriberInterface`. For most of us, that might never be the case. That said, it's a design decision for you to take. ### Using PluginAPIManager as a dependency

Our last version `EventManager` is going to push this idea that it has a separate job even further. We're going to remove the direct relationship between `PluginAPIManager` and `EventManager`. Instead, `EventManager` will use `PluginAPIManager` as a dependency. ```
class EventManager
{
    /**
     * The WordPress plugin API manager.
     *
     * @var PluginAPIManager
     */
    private $plugin_api_manager;

    /**
     * Constructor.
     *
     * @param PluginAPIManager $plugin_api_manager
     */
    public function __construct(PluginAPIManager $plugin_api_manager)
    {
        $this->plugin_api_manager = $plugin_api_manager;
    }
}
```

This is a big change. Our `EventManager` didn't even have a constructor before. Now, it has one that takes an instance of `PluginAPIManager` as an argument. #### Focusing on the unique event management language

Because we're not tied to the plugin API anymore, we don't have to use its language anymore either. Instead, we can use the language that we described earlier. The one that focuses on event listeners and event subscribers. ```
class EventManager
{
    // ...

    /**
     * Adds the given event listener to the list of event listeners
     * that listen to the given event.
     *
     * @param string   $event_name
     * @param callable $listener
     * @param int      $priority
     * @param int      $accepted_args
     */
    public function add_listener($event_name, $listener, $priority = 10, $accepted_args = 1)
    {
        $this->plugin_api_manager->add_callback($event_name, $listener, $priority, $accepted_args);
    }
}
```

Let's look at a new method called `add_listener`. The method itself isn't any different from what you've seen so far. It's just a wrapper around `add_callback`. What's changed is the language surrounding `add_listener`. It describes what our event management system is doing and not the plugin API. We're not adding a callback to a hook. Instead, we're adding an event listener to a list of event listeners for a specific event. The parameter names have also changed to reflect this new language. `hook_name` is now `event_name`. Meanwhile, we changed `callback` to `listener`. #### Updating our SubscriberInterface

This language change also carries over to our `SubscriberInterface`. `get_subscribed_hooks` isn't a good method name to use anymore. We're going to rename it to `get_subscribed_events` and update its documentation. ```
interface SubscriberInterface
{
    /**
     * Returns an array of events that this subscriber wants to listen to.
     *
     * The array key is the event name. The value can be:
     *
     *  * The method name
     *  * An array with the method name and priority
     *  * An array with the method name, priority and number of accepted arguments
     *
     * For instance:
     *
     *  * array('event_name' => 'method_name')
     *  * array('event_name' => array('method_name', $priority))
     *  * array('event_name' => array('method_name', $priority, $accepted_args))
     *
     * @return array
     */
    public static function get_subscribed_events();
}
```

We also need to add the `SubscriberInterface` methods from our previous versions of `EventManager`. That said, we'll have to make a few changes. These methods were using plugin API terminology which we're not using anymore. ```
class EventManager
{
    // ...

    /**
     * Adds an event subscriber.
     *
     * The event manager adds the given subscriber to the list of event listeners 
     * for all the events that it wants to listen to.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_events() as $event_name => $parameters) {
            $this->add_subscriber_listener($subscriber, $event_name, $parameters);
        }
    }

    /**
     * Adds the given subscriber listener to the list of event listeners
     * that listen to the given event.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $event_name
     * @param mixed               $parameters
     */
    private function add_subscriber_listener(SubscriberInterface $subscriber, $event_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->add_listener($event_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->add_listener($event_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10, isset($parameters[2]) ? $parameters[2] : 1);
        }
    }
}
```

As you can see above, we replaced all uses of the term "callback" with "event" or "listener". We also renamed `get_subscribed_hooks` since we changed the method name in the interface. We also rewrote the documentation to use our new event specific language. #### Code for this version

Below, you'll find a copy of the complete version of the `EventManager` class. ```
class EventManager
{
    /**
     * The WordPress plugin API manager.
     *
     * @var PluginAPIManager
     */
    private $plugin_api_manager;

    /**
     * Constructor.
     *
     * @param PluginAPIManager $plugin_api_manager
     */
    public function __construct(PluginAPIManager $plugin_api_manager)
    {
        $this->plugin_api_manager = $plugin_api_manager;
    }

    /**
     * Adds the given event listener to the list of event listeners
     * that listen to the given event.
     *
     * @param string   $event_name
     * @param callable $listener
     * @param int      $priority
     * @param int      $accepted_args
     */
    public function add_listener($event_name, $listener, $priority = 10, $accepted_args = 1)
    {
        $this->plugin_api_manager->add_callback($event_name, $listener, $priority, $accepted_args);
    }

    /**
     * Adds an event subscriber.
     *
     * The event manager adds the given subscriber to the list of event listeners 
     * for all the events that it wants to listen to.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_events() as $event_name => $parameters) {
            $this->add_subscriber_listener($subscriber, $event_name, $parameters);
        }
    }

    /**
     * Removes the given event listener from the list of event listeners
     * that listen to the given event.
     *
     * @param string   $event_name
     * @param callable $listener
     * @param int      $priority
     */
    public function remove_listener($event_name, $listener, $priority = 10)
    {
        $this->plugin_api_manager->remove_callback($event_name, $listener, $priority);
    }

    /**
     * Removes an event subscriber.
     *
     * The event manager removes the given subscriber from the list of event listeners 
     * for all the events that it wants to listen to.
     *
     * @param SubscriberInterface $subscriber
     */
    public function remove_subscriber(SubscriberInterface $subscriber)
    {
        foreach ($subscriber->get_subscribed_events() as $event_name => $parameters) {
            $this->remove_subscriber_listener($subscriber, $event_name, $parameters);
        }
    }

    /**
     * Adds the given subscriber listener to the list of event listeners
     * that listen to the given event.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $event_name
     * @param mixed               $parameters
     */
    private function add_subscriber_listener(SubscriberInterface $subscriber, $event_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->add_listener($event_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->add_listener($event_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10, isset($parameters[2]) ? $parameters[2] : 1);
        }
    }

    /**
     * Adds the given subscriber listener to the list of event listeners
     * that listen to the given event.
     *
     * @param SubscriberInterface $subscriber
     * @param string              $event_name
     * @param mixed               $parameters
     */
    private function remove_subscriber_listener(SubscriberInterface $subscriber, $event_name, $parameters)
    {
        if (is_string($parameters)) {
            $this->remove_listener($event_name, array($subscriber, $parameters));
        } elseif (is_array($parameters) && isset($parameters[0])) {
            $this->remove_listener($event_name, array($subscriber, $parameters[0]), isset($parameters[1]) ? $parameters[1] : 10);
        }
    }
}
```

### Showing you design tradeoffs

So what's the point of exploring these different versions of `EventManager`? Well, it's about showing you possible solutions to the same problem. This is all part of [teaching the design process](https://carlalexander.ca/creativity-and-teaching-programming/). All three version are correct and use pretty much the same code. The differences between them come down to different design philosophies. Which one you use comes down to your personal preferences. The first version requires a lot less thinking than the last one. But the last one allows you to use a richer and more descriptive language. The first one is more rigid and not as easy to extend. The last one is as flexible and decoupled as we can make it. The second one lives between those two extremes. That said, everything is up to you in the end. *You* have to decide how you want to solve the problem. You have to judge how each solution fits your particular context and pick the one that works best within it. ## What if you want to chain events?

So far, we've avoided a pretty important scenario. How do you call other event listeners within your own? You might want to apply changes to your data or even trigger your own event. Except that, you can't! Your classes that use the `SubscriberInterface` don't touch the plugin API anymore. So how can we make our event management system work around that constraint? ### Passing the EventManager to our subscriber

There are a couple of solutions to this problem. We won't look at them all today (I can hear your sigh of relief!). That said, they all have something in common. They pass our `EventManager` to our event subscribers. After all, we worked hard to create a class in charge of the plugin API! We don't want to change that. So it needs to make it into our event subscribers somehow. So how can we do that? To solve this problem, we're going to look at something called "[setter injection](https://en.wikipedia.org/wiki/Dependency_injection#Setter_injection)". This is a type of dependency injection where you can add a class dependency using a `set` method. This might seem complicated, but don't worry. You don't need a strong understanding of dependency injection itself to use setter injection. It comes down to creating a larger contract for `SubscriberInterface`. ### Extending our SubscriberInterface

So how do we create a larger contract for `SubscriberInterface`? We use [interface inheritance](https://carlalexander.ca/using-inheritance-wordpress/) and extend it. That's how! This is an ideal scenario to show you this more obscure object-oriented feature. ```
interface EventManagerAwareSubscriberInterface extends SubscriberInterface
{
    /**
     * Set the WordPress event manager for the subscriber.
     *
     * @param EventManager $event_manager
     */
    public function set_event_manager(EventManager $event_manager);
}
```

So first things first, why name it `EventManagerAwareSubscriberInterface`? Good question! We use the term "aware" to highlight a relationship with another class or interface. In this case, `EventManagerAwareSubscriberInterface` wants to highlight the relationship between `SubscriberInterface` and `EventManager`. Or put another way, `EventManagerAwareSubscriberInterface` is "aware" of the existence of `EventManager`. That's why we use the term "aware". From a code perspective, `EventManagerAwareSubscriberInterface` just adds one new method to the `SubscriberInterface`. That's the `set_event_manager` method. It takes an instance of `EventManager` as an argument. This is the method that we'll use for setter injection. This new method also changes the basic `SubscriberInterface` contract in a significant way. Before, we just needed to create the `get_subscribed_hooks` static method. And that method just needed to return an array with values in it. It didn't have much of an impact on the class itself. But `set_event_manager` has a larger impact on how you build classes that implement it. While you can't put variables inside an interface, you might still need them to make it work and do its job. This is the case here. `set_event_manager` requires that you store the `EventManager` inside the class. Let's look at how that can look. ### Creating a manager aware abstract class

This is also an ideal scenario for an [abstract class](https://carlalexander.ca/polymorphism-wordpress-abstract-classes/). The logic behind `set_event_manager` is something that's reusable by all classes implementing `EventManagerAwareSubscriberInterface`. So why not just create an abstract class to contain that reusable logic!? ```
abstract class AbstractEventManagerAwareSubscriber implements EventManagerAwareSubscriberInterface
{
    /**
     * The WordPress event manager.
     *
     * @var EventManager
     */
    protected $event_manager;

    /**
     * Set the WordPress event manager for the subscriber.
     *
     * @param EventManager $event_manager
     */
    public function set_event_manager(EventManager $event_manager)
    {
        $this->event_manager = $event_manager;
    }
}
```

That's what `AbstractEventManagerAwareSubscriber` does. It just contains the implemented `set_event_manager` method and an internal `event_manager` variable. All that the method does is assign the passed `EventManager` object to that variable. ### Updating our EventManager class

The last thing to do is to update our `EventManager` class. That's where the setter injection will take place. To be more precise, it takes place in our `add_subscriber` method. ```
class EventManager extends PluginAPIManager
{
    /**
     * Add an event subscriber.
     *
     * The event manager registers all the hooks that the given subscriber
     * wants to register with the WordPress Plugin API.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        if ($subscriber instanceof EventManagerAwareSubscriberInterface) {
            $subscriber->set_event_manager($this);
        }

        foreach ($subscriber->get_subscribed_hooks() as $hook_name => $parameters) {
            $this->add_subscriber_callback($subscriber, $hook_name, $parameters);
        }
    }

    // ...
}
```

This is what setter injection looks like for our event management system. We just check if the given subscriber implements `EventManagerAwareSubscriberInterface`. If it does, we call `set_event_manager` and pass it the instance of `EventManager` itself. It's as simple as that! ### What about our third EventManager?

You might have noticed already, but it's worth pointing out in case you didn't. The code you've seen so far doesn't work with our third version of the `EventManager`. That's because the relationship is different. Instead of having a relationship with `EventManager`, we need it to be with `PluginAPIManager`. This makes sense with everything that we've discussed surrounding the event management language. `EventManager` doesn't care about the plugin API in that scenario. It's the `PluginAPIManager` that does. ```
interface PluginAPIManagerAwareSubscriberInterface extends SubscriberInterface
{
    /**
     * Set the WordPress Plugin API manager for the subscriber.
     *
     * @param PluginAPIManager $plugin_api_manager
     */
    public function set_plugin_api_manager(PluginAPIManager $plugin_api_manager);
}

abstract class AbstractPluginAPIManagerAwareSubscriber implements PluginAPIManagerAwareSubscriberInterface
{
    /**
     * WordPress Plugin API manager.
     *
     * @var PluginAPIManager
     */
    protected $plugin_api_manager;

    /**
     * Set the WordPress Plugin API manager for the subscriber.
     *
     * @param PluginAPIManager $plugin_api_manager
     */
    public function set_plugin_api_manager(PluginAPIManager $plugin_api_manager)
    {
        $this->plugin_api_manager = $plugin_api_manager;
    }
}

class EventManager
{
    // ...

    /**
     * Adds an event subscriber.
     *
     * The event manager adds the given subscriber to the list of event listeners
     * for all the events that it wants to listen to.
     *
     * @param SubscriberInterface $subscriber
     */
    public function add_subscriber(SubscriberInterface $subscriber)
    {
        if ($subscriber instanceof PluginAPIManagerAwareSubscriberInterface) {
            $subscriber->set_plugin_api_manager($this->plugin_api_manager);
        }

        foreach ($subscriber->get_subscribed_events() as $event_name => $parameters) {
            $this->add_subscriber_listener($subscriber, $event_name, $parameters);
        }
    }
}
```

Here's all the code that we've seen so far, but modified for our other `EventManager` version. There aren't too many changes to highlight. We replaced all mentions of `EventManagerAware` with `PluginAPIManagerAware`. That's for both the interface and the abstract class. We also renamed the `set_event_manager` to `set_plugin_api_manager`. The `add_subscriber` method calls it, but doesn't pass itself to the subscriber anymore. Instead, it passes it the `PluginAPIManager` instance stored inside our `EventManager` class. # Putting our event management system to work

Let's look at a small example to show you how to use the event management system. We won't do anything too complicated for this article. We're just going to create an event subscriber that adds a new error to the login page. ## Initializing our event manager

```
$event_manager = new EventManager();
```

First, we need to initialize our `EventManager`. We're going to use a simpler version of it without the event specific language. That means that we'll also use the version of `SubscriberInterface` with `get_subscribed_hooks`. ## Creating our LoginErrorSubscriber class

```
class LoginErrorSubscriber implements SubscriberInterface
{
    // Empty!
}
```

Here's our empty `LoginErrorSubscriber` class implementing our `SubscriberInterface`. Its job is to add support for a custom error on the WordPress login screen. We want it to display a custom error message while also doing the shake animation. ```
class LoginErrorSubscriber implements SubscriberInterface
{
    /**
     * Returns an array of hooks that this subscriber wants to register with
     * the WordPress plugin API.
     *
     * @return array
     */
    public static function get_subscribed_hooks()
    {
        return array(
            'shake_error_codes' => 'add_error_code',
            'wp_login_errors' => 'add_error',
        );
    }
}
```

To do that, we need to subscribe to two hooks: [`shake_error_codes`](https://developer.wordpress.org/reference/hooks/shake_error_codes/) and [`wp_login_errors`](https://developer.wordpress.org/reference/hooks/wp_login_errors/). We do that through our trusty `get_subscribed_hooks` method. It tells the event manager to subscribe two `LoginErrorSubscriber` methods with the plugin API. These are `add_error` and `add_error code`. ```
class LoginErrorSubscriber implements SubscriberInterface
{
    /**
     * The error code that our login error subscriber creates.
     */
    const ERROR_CODE = 'my-login-error-code';

    // ...
}
```

We're going to start by adding an error code constant to our `LoginErrorSubscriber` class. It stores the custom error code that we want it to manage. We can then use it in the `add_error` and `add_error code` methods. ```
class LoginErrorSubscriber implements SubscriberInterface
{
    // ...

    /**
     * Add our error to the login errors.
     *
     * @param WP_Error $error
     *
     * @return WP_Error
     */
    public function add_error(WP_Error $error)
    {
        if (!isset($_GET[self::ERROR_CODE])) {
            return $error;
        }

        $error->add(self::ERROR_CODE, 'The error message displayed on the login screen.');

        return $error;
    }
}
```

`add_error` is the method that adds the error message that we want to display on the login page. To do that, it uses [`isset`](https://secure.php.net/manual/en/function.isset.php) to inspect the `$_GET` global variable to see if our error code is there. If it isn't, it returns the instance of [`WP_Error`](https://codex.wordpress.org/Class_Reference/WP_Error) passed to the method. If our error code is there, we add our error message to the `WP_Error` using its `add` method. You need to pass it our `ERROR_CODE` constant as the first argument. The second argument is the error message that you want to display. We then return the modified `WP_Error` object. ```
class LoginErrorSubscriber implements SubscriberInterface
{
    // ...

    /**
     * Add our error code to the handled login error codes.
     *
     * @param array $error_codes
     *
     * @return array
     */
    public function add_error_code(array $error_codes)
    {
        $error_codes[] = self::ERROR_CODE;

        return $error_codes;
    }
}
```

`add_error_code` is the other method that we subscribe with the plugin API. Its job is to add our `ERROR_CODE` constant to the list of valid error codes for the login page. If we didn't do this, the login form wouldn't shake when it displayed our error message. (And why have a login form error if it doesn't make the form shake!?) ## Adding our LoginErrorSubscriber class to our event manager

```
$event_manager = new EventManager();
$event_manager->add_subscriber(new LoginErrorSubscriber());
```

The last step is to add our `LoginErrorSubscriber` to `EventManager`. We just create a new instance of it inside the call to the `add_subscriber` method. And this will add your new error message on the WordPress login page. ![Login Error](https://carlalexander.ca/wp-content/uploads/2016/01/Screen-Shot-2016-01-22-at-8.57.19-AM.png)# The foundation of object-oriented WordPress

When you code with WordPress, you can't escape the need to use the plugin API. This doesn't change when you want to use object-oriented programming with WordPress either. You just can't escape it. It's as much a part of WordPress as the GPL. The event management system acknowledges this reality. It's like having a great boss. A great boss lets you do your best work while shielding you from management needs. The event management system shields you from having to always worry about the plugin API. That way, you can focus on designing meaningful classes that don't have to depend on it. That's why it's the foundation of object-oriented WordPress. And now you can use it now too! You can refer to [the code from this article](https://gist.github.com/carlalexander/d439be349fffe01cd381) as a starting point.