How to Remove a Hook from a Class-based WordPress Plugin

Class Object Oriented PHP

Are you trying to modify the functionality of a 3rd party plugin without actually editing the code of their plugin (which would result in losing your customization upon the next update)?  Having trouble with it because that plugin is object oriented or class-based?

I have run into this situation multiple times, and finally found a really good fix for it.

In this post, I’ll show you how to remove an action or filter hook from another plugin even though that plugin is class-based.

Basic Removing of Actions and Filters

You probably already know that you can remove someone’s add_action()  using a remove_action()  and you can remove someone’s add_filter()  using remove_filter() .

So, in normal circumstances, it’s very simple.  In someone’s plugin, they have something like…

add_action('save_post', 'my_action_function');
add_filter('the_content', 'my_filter_function');

… and you just remove them using…

remove_action('save_post', 'my_action_function');
remove_filter('the_content', 'my_filter_function');

Class-based Plugins are a bit more Complicated

A plugin that uses classes looks something like this…

class My_Plugin_Class {
 
	function __construct() {
		add_action( 'save_post', array( $this, 'my_action_function' ), 11);
        }
 
        function my_action_function(){
         //do something
        }
 
}
 
$my_plugin_class = new My_Plugin_Class();

Notice that add_action doesn’t just have a simple string of the function callback, but instead it has an array with $this and then that function callback string.  This allow plugin developers to use simple function names without the worry of conflicting with other plugins because they are limited to the scope of that class.

So, how do you remove that?

How to Remove a Hook from Within the Class-based Plugin

If you can hook into that plugin, then you should be able to use this method to gain access to the class object instance in order to remove the hook.

global $my_plugin_class; // Get access to the class object instance
remove_action('save_post', array($my_plugin_class, 'my_action_function'), 11); // Remove the class hook using the same priority

How to Remove a Hook from Outside the Class-based Plugin

If you are not able to hook into the class-based plugin, then try to use the class name as a string in the first part of the callback array, like this…

remove_action('save_post', array('My_Plugin_Class', 'my_action_function'), 11); // Remove the class hook using the same priority

If None of Those Methods Work, Use These Functions

Sometimes, it just won’t work.  But, here’s a couple nice helper functions that work great.

Credit to tripflex.

<?php
/**
 * Make sure the function does not exist before defining it
 */
if( ! function_exists( 'remove_class_filter' ) ){
	/**
	* Remove Class Filter Without Access to Class Object
	*
	* In order to use the core WordPress remove_filter() on a filter added with the callback
	* to a class, you either have to have access to that class object, or it has to be a call
	* to a static method.  This method allows you to remove filters with a callback to a class
	* you don't have access to.
	*
	* Works with WordPress 1.2+ (4.7+ support added 9-19-2016)
	* Updated 2-27-2017 to use internal WordPress removal for 4.7+ (to prevent PHP warnings output)
	*
	* @param string $tag         Filter to remove
	* @param string $class_name  Class name for the filter's callback
	* @param string $method_name Method name for the filter's callback
	* @param int    $priority    Priority of the filter (default 10)
	*
	* @return bool Whether the function is removed.
	*/
	function remove_class_filter( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
        global $wp_filter;
        // Check that filter actually exists first
        if ( ! isset( $wp_filter[ $tag ] ) ) {
            return FALSE;
        }
        /**
        * If filter config is an object, means we're using WordPress 4.7+ and the config is no longer
        * a simple array, rather it is an object that implements the ArrayAccess interface.
        *
        * To be backwards compatible, we set $callbacks equal to the correct array as a reference (so $wp_filter is updated)
        *
        * @see https://make.wordpress.org/core/2016/09/08/wp_hook-next-generation-actions-and-filters/
        */
        if ( is_object( $wp_filter[ $tag ] ) && isset( $wp_filter[ $tag ]->callbacks ) ) {
            // Create $fob object from filter tag, to use below
            $fob       = $wp_filter[ $tag ];
            $callbacks = &$wp_filter[ $tag ]->callbacks;
        } else {
            $callbacks = &$wp_filter[ $tag ];
        }
        // Exit if there aren't any callbacks for specified priority
        if ( ! isset( $callbacks[ $priority ] ) || empty( $callbacks[ $priority ] ) ) {
            return FALSE;
        }
        // Loop through each filter for the specified priority, looking for our class & method
        foreach ( (array) $callbacks[ $priority ] as $filter_id => $filter ) {
            // Filter should always be an array - array( $this, 'method' ), if not goto next
            if ( ! isset( $filter['function'] ) || ! is_array( $filter['function'] ) ) {
                continue;
            }
            // If first value in array is not an object, it can't be a class
            if ( ! is_object( $filter['function'][0] ) ) {
                continue;
            }
            // Method doesn't match the one we're looking for, goto next
            if ( $filter['function'][1] !== $method_name ) {
                continue;
            }
            // Method matched, now let's check the Class
            if ( get_class( $filter['function'][0] ) === $class_name ) {
                // WordPress 4.7+ use core remove_filter() since we found the class object
                if ( isset( $fob ) ) {
                    // Handles removing filter, reseting callback priority keys mid-iteration, etc.
                    $fob->remove_filter( $tag, $filter['function'], $priority );
                } else {
                    // Use legacy removal process (pre 4.7)
                    unset( $callbacks[ $priority ][ $filter_id ] );
                    // and if it was the only filter in that priority, unset that priority
                    if ( empty( $callbacks[ $priority ] ) ) {
                        unset( $callbacks[ $priority ] );
                    }
                    // and if the only filter for that tag, set the tag to an empty array
                    if ( empty( $callbacks ) ) {
                        $callbacks = array();
                    }
                    // Remove this filter from merged_filters, which specifies if filters have been sorted
                    unset( $GLOBALS['merged_filters'][ $tag ] );
                }
                return TRUE;
            }
        }
        return FALSE;
	}
}
/**
 * Make sure the function does not exist before defining it
 */
if( ! function_exists( 'remove_class_action') ){
    /**
    * Remove Class Action Without Access to Class Object
    *
    * In order to use the core WordPress remove_action() on an action added with the callback
    * to a class, you either have to have access to that class object, or it has to be a call
    * to a static method.  This method allows you to remove actions with a callback to a class
    * you don't have access to.
    *
    * Works with WordPress 1.2+ (4.7+ support added 9-19-2016)
    *
    * @param string $tag         Action to remove
    * @param string $class_name  Class name for the action's callback
    * @param string $method_name Method name for the action's callback
    * @param int    $priority    Priority of the action (default 10)
    *
    * @return bool               Whether the function is removed.
    */
    function remove_class_action( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
        remove_class_filter( $tag, $class_name, $method_name, $priority );
    }
}

With these functions available in your code, you can use them like this…

remove_class_action('My_Plugin_Class', 'my_action_function', 11); // Remove the class hook using the same priority

Questions?  Help?

Hopefully, this post solved your problem.  If so, hit me up in the comments below.

Run into any issues?  Let me know in the comments, and I’d love to help.

Subscribe
Notify of
guest

2 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Shahzaib
Shahzaib
3 years ago

Hi there,

I am trying to remove the action below within the given class and it did not work

class Dokan_Template_Products {

public static $errors;
public static $product_cat;
public static $post_content;

function __construct() {
//Bunch of other actions……
………….
add_action( ‘dokan_product_edit_after_inventory_variants’, array( __CLASS__, ‘load_others_template’ ), 85, 2 );
}
Can you suggest a way to remove it? Also how can I override the action?

Thank you