---
title: "Part 7 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: Managing WordPress Hooks"
date: 2022-02-04
author: "Tassos Antoniou"
featured_image: "https://pressidium.com/wp-content/uploads/2021/07/Part-7-WordPress-and-Object-Oriented-Programming-a-WordPress-Example-Implementation-Managing-WordPress-hooks-1.png"
categories:
  - name: "Tutorials"
    url: "/blog/category/tutorials.md"
url: "https://pressidium.com/blog/part-7-wordpress-and-object-oriented-programming/"
---

# Part 7 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: Managing WordPress Hooks

Up to this point, interacting with the [Plugin API](https://codex.wordpress.org/Plugin_API) meant calling `add_action()` and `add_filters()` in the constructor of each class.

So far that approach was good enough, as it kept things simple and allowed us to focus on learning more about object-oriented programming with WordPress. However, it’s not ideal.

If an object registers all of its hooks when it’s created, things like unit testing become tricky.

**NOTE:** Unit tests should test each “unit” in isolation. Even if you’re not writing unit tests at the moment, writing testable code will save you a lot of time refactoring later, if you ever decide to write tests.

## The Hooks Manager

Let’s take this one step further and introduce a new class to manage our hooks, we’ll call it `Hooks_Manager`. This class is going to be responsible for the registration of all of our hooks. So, we’ll create a new class with a `register()` method.

```
class Hooks_Manager {

    /**
     * Register the hooks of the given object.
     *
     * @param object $object
     */
    public function register( $object ) {
        // Register the hooks the specified object needs
    }

}
```

We need an interface for each class that needs to register hooks to implement.

```
interface Hooks {

    /**
     * Return the actions to register.
     *
     * @return array
     */
    public function get_actions();

}
```

You can think of an interface as a *contract*, where a class that implements that interface is “contractually bound” to implement all methods defined in that interface.

For example, a `Login_Error` class that hooks into the `login_head` action, *must* implement the `get_actions()` method of our `Hooks` interface.

```
class Login_Error implements Hooks {

    public function get_actions() {
        return array(
            'login_head' => array( 'add_errors', 10, 1 ),
        );
    }

}
```

The `register()` method of `Hooks_Manager` accepts an object, calls its `get_actions()` method and registers all of its actions.

```
public function register( $object ) {
    $actions = $object->get_actions();

    foreach ( $actions as $action_name => $action_details ) {
        $method        = $action_details[0];
        $priority      = $action_details[1];
        $accepted_args = $action_details[2];

        add_action(
            $action_name,
            array( $object, $method ),
            $priority,
            $accepted_args
        );
    }
}
```

Let’s add a `get_filters()` method to our interface, so we can register both actions and filters.

```
interface Hooks {

    /**
     * Return the actions to register.
     *
     * @return array
     */
    public function get_actions();

    /**
     * Return the filters to register.
     *
     * @return array
     */
    public function get_filters();

}
```

Back to our `Login_Error` class, we need to implement this new `get_filters()` method.

```
class Login_Error implements Hooks {

    public function get_actions() {
        return array(
            'login_head' => array( 'add_errors', 10, 1 ),
        );
    }
    public function get_filters() {
        return array(
            'authenticate'     => array( 'track_credentials', 10, 3 ),
            'shake_error_code' => array( 'add_error_code', 10, 1 ),
            'login_errors'     => array( 'format_error_message', 10, 1 ),
        );
    }

}
```

We’ll rename the `register()` method of our `Hooks_Manager` to `register_actions()`. We’ll also add a `register_filters()` method. These two methods will be responsible for registering actions and filters respectively.

```
class Hooks_Manager {
    
    /**
     * Register the actions of the given object.
     *
     * @param object $object
     */
    private function register_actions( $object ) {
        $actions = $object->get_actions();

        foreach ( $actions as $action_name => $action_details ) {
            $method        = $action_details[0];
            $priority      = $action_details[1];
            $accepted_args = $action_details[2];

            add_action(
                $action_name,
                array( $object, $method ),
                $priority,
                $accepted_args
            );
        }
    }
    
    /**
     * Register the filters of the given object.
     *
     * @param object $object
     */
    private function register_filters( $object ) {
        $filters = $object->get_filters();

        foreach ( $filters as $filter_name => $filter_details ) {
            $method        = $filter_details[0];
            $priority      = $filter_details[1];
            $accepted_args = $filter_details[2];

            add_filter(
                $filter_name,
                array( $object, $method ),
                $priority,
                $accepted_args
            );
        }
    }
    
}
```

Now we can add a `register()` method again, which is simply going to call both `register_actions()` and `register_filters()`.

```
class Hooks_Manager {

    /**
     * Register an object.
     *
     * @param object $object
     */
    public function register( $object ) {
        $this->register_actions( $object );
        $this->register_filters( $object );
    }
    
    // ...
```

What if a class doesn’t need to register both actions and filters? The `Hooks` interface contains two methods: `get_actions()` and `get_filters()`. All classes that implement that interface will be forced to implement *both* methods.

```
class Cookie_Login implements Hooks {

    public function get_actions() {
        return array(
            'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ),
            'auth_cookie_bad_hash'     => array( 'handle_bad_hash', 10, 1 ),
            'auth_cookie_valid'        => array( 'handle_valid', 10, 2 ),
        );
    }

    public function get_filters() {
        return array();
    }

}
```

For example, the `Cookie_Login` class has to register only actions, but it’s now forced to implement the `get_filters()` method just to return an empty array.

The *Interface Segregation Principle (ISP)*, the “I” in S.O.L.I.D, states:

*“No client should be forced to depend on methods it does not use.”*

Meaning that what we’re doing now is exactly what we shouldn’t be doing.

## **Interface Segregation**

We can fix this by splitting our interface into smaller, more specific ones so our classes will only have to know about the methods that are of interest to them.

```
interface Actions {


    /**
     * Return the actions to register.
     *
     * @return array
     */
    public function get_actions();

}

```

```
interface Filters {


    /**
     * Return the filters to register.
     *
     * @return array
     */
    public function get_filters();

}

```

We don’t need both `get_actions()` and `get_filters()` anymore, we can implement only the `Actions` interface and get rid of `get_filters()`

```
class Cookie_Login implements Actions {

    public function get_actions() {
        return array(
            'auth_cookie_bad_username' => array( 'handle_bad_username', 10, 1 ),
            'auth_cookie_bad_hash'     => array( 'handle_bad_hash', 10, 1 ),
            'auth_cookie_valid'        => array( 'handle_valid', 10, 2 ),
        );
    }

}
```

On the other hand, `Login_Error`, which needs actions *and* filters, just has to implement both interfaces. Classes may implement more than one interface by separating them with a comma.

```
class Login_Error implements Actions, Filters {

    public function get_actions() {
        return array(
            'login_head' => array( 'add_errors', 10, 1 ),
        );
    }

    public function get_filters() {
        return array(
            'authenticate'     => array( 'track_credentials', 10, 3 ),
            'shake_error_code' => array( 'add_error_code', 10, 1 ),
            'login_errors'     => array( 'format_error_message', 10, 1 ),
        );
    }

}
```

Now that we’ve segregated our interface, we just have to update the `register()` method of `Hooks_Manager` to reflect our changes.

```
class Hooks_Manager {

    /**
     * Register an object.
     *
     * @param object $object
     */
    public function register( $object ) {
        if ( $object instanceof Actions ) {
            $this->register_actions( $object );
        }
        
        if ( $object instanceof Filters ) {
            $this->register_filters( $object );
        }
    }
    
    // ...
```

That way, we conditionally call only `register_actions()`, only `register_filters()`, or both, based on the interface(s) the specified object implements.

                    

Try our Award-Winning WordPress Hosting today!

 

 [START YOUR FREE TRIAL](https://pressidium.com/free-trial/?utm_content=inlinebanner&utm_source=pressidiumcom&utm_medium=blog&utm_campaign=tutorials) 

 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/views/blocks/inner-banner-backgrounds/speed-1.jpg) ![](https://pressidium.com/wp-content/themes/pressidium-com/views/blocks/inner-banner-backgrounds/speed-1-mobile.jpg) 

To actually use the hooks manager:

```
$hooks_manager = new Hooks_Manager();
$hooks_manager->register( $login_error );
$hooks_manager->register( $cookie_login );
```

That’s it! We can now use that object to manage hooks across the entire codebase.

## **Conclusion**

Of course, there are several ways to manage your hooks in an object-oriented way, we just showed you one of them. You should experiment and find one that fits your needs.

Stay with us for the [**last part of our Objected Oriented Programming Series**](https://pressidium.com/blog/part-8-wordpress-and-object-oriented-programming/), where we’ll see **how to handle options** in an object-oriented way. We will talk about encapsulation, abstraction and how to decouple your classes to create a flexible plugin that’s easy to extend!

## See Also

- [Summary of the series](https://pressidium.com/blog/oop-wordpress-plugin-object-oriented-programming-overview-tutorial/ "Summary of the series")
- [Part 1 – WordPress and Object-Oriented Programming – An Overview](https://pressidium.com/blog/wordpress-and-object-oriented-programming/ "Part 1 – WordPress and Object-Oriented Programming – An Overview")
- [Part 2 – WordPress and Object Oriented Programming: A Real World Example](https://pressidium.com/blog/part-2-wordpress-and-object-oriented-programming/ "Part 2 – WordPress and Object Oriented Programming: A Real World Example")
- [Part 3 – WordPress and Object Oriented Programming: Α WordPress Example – Defining the Scope](https://pressidium.com/blog/part-3-wordpress-and-object-oriented-programming/ "Part 3 – WordPress and Object Oriented Programming:  Α WordPress Example – Defining the Scope")
- [Part 4 – WordPress and Object Oriented Programming: A WordPress Example – Design](https://pressidium.com/blog/part-4-wordpress-and-object-oriented-programming/ "Part 4 – WordPress and Object Oriented Programming: A WordPress Example – Design")
- [Part 5 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: The Administration Menu](https://pressidium.com/blog/part-5-wordpress-and-object-oriented-programming/ "Part 5 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: The Administration Menu")
- [Part 6 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: Registering the Sections](https://pressidium.com/blog/part-6-wordpress-and-object-oriented-programming/ "Part 6 – WordPress and Object Oriented Programming: A WordPress Example – Implementation: Registering the Sections")
 
 Start Your 14 Day Free Trial

 Try our award winning WordPress Hosting!

 [ START YOUR FREE TRIAL ](https://pressidium.com/free-trial/?utm_source=pressidiumcom&utm_medium=blog&utm_campaign=tutorials) [ VIEW OUR PRICE PLANS ](https://pressidium.com/plans/?utm_source=pressidiumcom&utm_medium=blog&utm_campaign=tutorials) 

 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/assets/pages/blog/partners/1.png) 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/assets/pages/blog/partners/2.png) 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/assets/pages/blog/partners/3.png) 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/assets/pages/blog/partners/4.png) 

 ![](https://pressidium.com/wp-content/themes/pressidium-com/assets/pages/blog/partners/5.png)