How to `remove_action` with Inaccessible Objects

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_action and add_filter whenever 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.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top