How can I remove a hooked action created by a plugin?

public function orddd_capabilities() {
        $role = get_role( 'shop_manager' );
        if ( $role != '' ) {
            $role->add_cap( 'manage_options' );
        }
    }
add_action( 'admin_init', array( &$this, 'orddd_capabilities' ) );

this action was added by a plugin and would always reapply the “manage_options” capabilities to shop_manager after I disable it using User Role Editor. How can I disable this action using the function.php of my child theme?

1 Answer
1

This one is tricky. You need to remove a hooked function which is a member of dynamically created class instance and the usual methods like remove_action( 'hook_name', 'hook_function', priority ) and even remove_action( 'hook_name', array( 'class_name', 'member_function' ), priority ) won’t work. I once had to solve a similar problem. Mine was more complicated, I had to change the priority of such a hooked function. After spending a couple of time I’ve finished up with the following code (modified according to your needs):

function get_hooked_function( $hook = '', $function = '' ) {
    global $wp_filter;
    if ( isset( $wp_filter[$hook]->callbacks ) ) {      
        array_walk( $wp_filter[$hook]->callbacks, function( $callbacks, $priority ) use ( &$hooks ) {           
            foreach ( $callbacks as $id => $callback )
                $hooks[] = array_merge( [ 'id' => $id, 'priority' => $priority ], $callback );
        });         
    } else {
        return NULL;
    }
    foreach( $hooks as &$item ) {
        // skip if callback does not exist
        if ( !is_callable( $item['function'] ) ) continue;

        // function name as string or static class method eg. 'Foo::Bar'
        if ( is_string( $item['function'] ) ) {
            if ( $item['function'] === $function ) return [ $function, $item['priority'] ];
        } elseif ( is_array( $item['function'] ) ) {
            if ( $item['function'][1] === $function ) return [ $item['function'], $item['priority'] ];
        }       
    }
    return NULL;
}
add_action( 'admin_init', function () {
    if ( $hooked_function = get_hooked_function( 'admin_init', 'orddd_capabilities' ) )
        remove_action( 'admin_init', $hooked_function[0], $hooked_function[1] );
}, 9 );

The get_hooked_function() function walks through global $wp_filter array of WP_Hook objects where all hooks are registered (to be more precise only through its item named as this function first parameter) and searches for the function named as its second parameter. When a hook is registered with a global function, it returns its name and registered priority, e.g.

$result = Array (
    [0] => register_admin_color_schemes
    [1] => 1
)

but when a hook is registered with a class member function, it returns an array like

$result = Array (
    [0] => Array (
        [0] => order_delivery_date Object
        [1] => orddd_capabilities
    )
    [1] => 10
)

(the basic idea of this function is taken from this stackoverflow answer, you can look there for additional technical details). Then the $result[0] array is applicable to use with remove_action() function. Next we should make sure that our code will be called after the hook is set by plugin, but before it is fired. The most simple way to do it is to register our code to the same hook with a slightly less priority. Since your plugin register its hook with the default priority 10, to deregister it we can use a priority of 9.

Please note that this solution won’t work with WordPress earlier than 4.7 due to this change in filter processing mechanism (though I believe you don’t use something that ancient).

Afterwords

If someone would search an answer on how to change the priority on such a hooked function, the solution is

add_action( '<HOOK_NAME>', function () {
    if ( $hooked_function = get_hooked_function( '<HOOK_NAME>', '<FUNCTION_NAME>' ) )
        if ( remove_action( '<HOOK_NAME>', $hooked_function[0], $hooked_function[1] ) )
            add_action( '<HOOK_NAME>', $hooked_function[0], <NEW_PRIORITY> );
}, <SOME_PRIORITY_LESS_THAN_MIN_OF_OLD_AND_NEW_PRIORITIES> );

Leave a Comment