WordPress plugin constructors seem to be more and more a topic of debate when it comes to what they should define. I’ve talked about it before but it’s okay to revisit a topic like this from time-to-time, right?
After all, there are things we learn and things that we change as we gain more experience.
It’s not at all uncommon to see plugins defining hooks and other behavior, but I’m not a fan of this approach. Instead, I think handling hook registration should be done in its own function or, even more drastically, handled by a set of classes.
But before getting into that, I want to explain what should go in a WordPress plugin constructor, why it should go in a constructor, and how this can be handled when working on your plugins.
WordPress Plugin Constructors
From the outset, I think that constructors should be used for one thing:
- Initializing the state of an object.
What defines an object’s initial state might depend on if it’s created “from scratch” or if it’s being loaded with information from a previous set (like a session being serialized). The way I see it:
- attributes are nouns that describe an object,
- functions are verbs that describe what the object can do.
The functions, of course, do the work that object is capable of doing. They may modify the state of the object when called, or they may do work on the arguments passed into the functions.
What Should Go in a Constructor?
When an object is constructed it should simply be set in such a way that its attributes are set and its functions are ready to do work.
If there’s something in the constructor that does not impact an object’s initial state, it should not be there.
Why Should Attributes Be in a Constructor?
Perhaps a better way to ask this question is:
Why shouldn’t hooks be defined in the constructor?
WordPress’ hook system is part of the event-driven design pattern (which I’m a fan of), but registering hooks doesn’t describe the state of the object. Instead, at the most fundamental level, it’s something that creates a relationship with the object and WordPress.
The object’s initial state does not need to know about WordPress, have any of its functions set to be coupled with WordPress, or need to do any processing with WordPress.
Remember, attributes are initialized in a constructor. WordPress is not an attribute. It’s a dependency. To create a dependency is to take an action which is the definition of a verb.
Thus, all hook registration should be done in a function.
How Can We Handle Hook Registration?
This is one of those topics that can be a post or a series of posts all its own.
- It’s possible to create a class that maintains a registry of objects and the hooks with WordPress.
- It’s also possible to define hook registration within a function in the class.
- We can also do a number of things with dependency inversion.
All of the above are things that are beyond the scope of this post but for the sake of simplicity, I’ll show can example of how a class can register its functions with WordPress in an init function:
This way, we’re able to instantiate the object, test it, use it, etc., but we don’t have to deal with anything related to WordPress without explicitly calling the init function.
Once that’s called, the dependency is created, WordPress is needed, and things get more complicated.
Oh, And That Testing Thing
I want to mention one more point that’s a bit beyond the scope and point of this post but is still relevant: When it comes to testing a class, we should be able to:
- create an instance of the class,
- testing its logic by calling functions,
- passing it parameters and evaluating its return values.
And we should be able to do as much of this as possible in isolation. If hooks are defined in the constructor, it creates an immediate dependency on WordPress which should not be needed.
WordPress does not describe the state of an object. It’s a dependency of the object.
Anyway, the point I’m trying to make is that WordPress plugin constructors should not handle the registration of hooks because hooks do not describe its state. They are related to something the class does and they prevent us from being able to test an object in isolation.
So they have their place, but it’s not in the constructor.