I see a lot of WordPress developers doing things like this:
<?php
class SomeClass {
// ...
public function __construct() {
add_action("init", array($this, "some_method")); // This is bad!
}
}
Why Using $this in add_action Is Bad
The problem with this code is that you don’t have access to that specific object ($this) outside the class, and most probably it’s a class from a plugin or a theme that you cannot directly modify. This makes it a lot difficult, to remove the action later because WordPress requires the exact same callback to remove_action as was used in add_action.
The add_action function stores the action along with the callback in WordPress’ hooks system, which you can later use remove_action to remove. However, when the callback is an instance method, as in the example above, you need access to the exact instance of the object ($this) to remove it. Without that exact instance, trying to call remove_action won’t work.
For example, if you try this:
remove_action("init", array(SomeClass::class, "some_method"));
It won’t work because remove_action expects the exact same object that was passed to add_action. Since $this refers to the specific instance of the class, and not the class itself, you don’t have access to it.
The Solution: Removing the Callback from the Global $wp_filter
To solve this problem, I created a helper function called remove_class_method_action. This function searches through the global $wp_filter for a specific hook, checks if the callback is tied to a class method, and removes the action if it matches.
Here’s the code for the function:
function remove_class_method_action($hook_name, $class_name, $method_name)
{
global $wp_filter;
if (isset($wp_filter[$hook_name]->callbacks)) {
foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) {
foreach ($callbacks as $key => $callback) {
if (is_array($callback["function"])) {
$object = $callback["function"][0];
$method = $callback["function"][1];
if (
is_object($object) &&
get_class($object) === $class_name &&
$method === $method_name
) {
remove_action(
$hook_name,
[$object, $method],
$priority
);
}
}
}
}
}
}
How It Works
The function takes three arguments:
$hook_name: The name of the action hook (e.g.,'init','wp_footer').$class_name: The class name where the method is located.$method_name: The specific method you want to remove from the hook.
It works by iterating through the $wp_filter global variable, which stores all the hooks and their callbacks. For each callback, it checks if it’s an array (which indicates it’s a class method), and if so, it compares the class and method names. Once it finds a match, it uses remove_action to remove the action.
Important: When to Call It
You need to call the function after the action you want to remove has been added, which means selecting the appropriate hook.
For example:
add_action("plugins_loaded", "remove_dlm_scripts");
function remove_dlm_scripts() {
remove_class_method_action(
"wp_footer",
"DLM_TC_Modal",
"add_footer_scripts"
);
}
In this example, I’m using the plugins_loaded hook because I knew that at that point, the action had already been added.
Good Practices for add_action and add_filter
To make your themes and plugins developer-friendly, make sure that all add_action and add_filter calls are reversible.
- Use static methods with
add_actionandadd_filterwhenever possible. - Avoid using closures.
- It’s fine to use objects, as long as they are accessible. But don’t put them in the global scope, as that’s another bad practice.