I get a call from a client. Their membership site is acting up: user permissions randomly failing, content not unlocking when it should. A total mess. They mention another developer built a custom plugin to handle the integration with their forum software, so that is my first stop. I open the main plugin file, find the primary class, and my stomach drops. The __construct method is over 200 lines long. It is a chaotic mess of add_action and add_filter calls. You cannot even instantiate the object without the entire WordPress environment lighting up. This is the classic mistake of putting WordPress hooks straight into a class.
A class constructor has one job: to create an object and set its initial state. It is not meant to be a switchboard operator connecting the class to every hook and filter in WordPress. When you jam all your hooks into the constructor, you get tight coupling. The class then depends completely on the WordPress environment just to exist, which means you cannot test it on its own and you cannot reuse it. It is a dead end.
The “quick fix” that solves nothing
My first thought was to refactor it for readability. I pulled all the hooks out of the constructor and moved them into a new method called init(). Then, right at the end of the constructor, I called $this->init();. Cleaner code. It felt good for about five minutes, until I realized I had not solved anything. All I had done was rearrange the furniture in a burning house. The constructor was still kicking everything off, so the class was just as coupled and untestable as before. It was a rookie move.
Decoupling your hooks properly
The fix is to separate creating the object from wiring it into WordPress. The class should not be responsible for hooking itself in; something else should. The class just provides the public methods that are available to be hooked. This is not a new idea. It is a foundational principle of good object-oriented design, one I saw laid out well over at Carl Alexander’s blog. You let the code that loads the plugin handle the wiring.
<?php
class Sensible_Plugin_Class {
// The constructor should only handle dependencies and initial state.
// public function __construct( $some_dependency ) {
// $this->dependency = $some_dependency;
// }
/**
* Wires up the hooks.
*/
public function init() {
add_action( 'wp_loaded', [ $this, 'on_wp_loaded' ] );
add_filter( 'the_content', [ $this, 'modify_the_content' ] );
}
public function on_wp_loaded() {
// Your logic for the 'wp_loaded' action...
}
public function modify_the_content( $content ) {
// Your logic for filtering content...
return $content;
}
}
// In your main plugin file, *outside* the class:
$my_plugin_instance = new Sensible_Plugin_Class();
$my_plugin_instance->init();So what’s the takeaway?
It comes down to control and separation of concerns. Your class is a blueprint for an object with specific capabilities. That is it. How that object gets used and integrated into the larger WordPress application is a separate responsibility. Keep the hooks out of the constructor and you get code that is:
- Testable: You can write unit tests for your methods without loading all of WordPress.
- Reusable: You could, in theory, use that same class in a different context.
- Maintainable: The next developer who sees your code (who might be you in six months) will immediately understand where the logic is versus where the integration is.
This stuff gets complicated fast. If you are tired of debugging someone else’s mess and just want your site to work, drop my team a line. We have probably seen it before.
Whatever else you do, keep your hooks out of the constructor.