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?

  • Note that the plugin uses &$this, it should be $this, the use of & is for compatibility with PHP v4 which hasn't worked with WordPress in decades. Which plugin is this? – Tom J Nowell Jul 1 '20 at 21:12
  • @TomJNowell I'm not the OP, but I think this is Order Delivery Date Pro from Tyche Software. Their coding style sometimes make me cry. – Ivan Shatsky Jul 1 '20 at 21:22
  • 2
    It may turn out that this can't be answered without a copy of the plugin and contacting their support channels, as to answer this would need to know what $this is referring to and if we can get references to it – Tom J Nowell Jul 1 '20 at 21:24

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).


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> );

  • How does this answer the question? It might work, but it's copypasta, and provides no understanding of what the function does. Can you edit the answer to explain what it does? I should be able to reproduce the code snippet based on your explanation given a working knowledge of WP APIs – Tom J Nowell Jul 1 '20 at 21:27
  • @TomJNowell This is exactly answers the OP's question, all he need is to copy this code to his functions.php. It isn't a copypasta from my code written for another customer, I'm already modified it according to his needs. I could try to give an explanation on how this code works, but since I'm not a native english speaker it would take a lot of time since the code is quite complicated. Your corrections to my PHP code is only a cosmetics, I don't use additional { ... } blocks for one-line statements. If you think it worth to give a -1 for that, ok, its up to you. – Ivan Shatsky Jul 1 '20 at 21:36
  • @TomJNowell Added an explanation of how this code works. You're right, although this explanation may be not needed for OP, it can help other users who search an answer to the similar questions. – Ivan Shatsky Jul 2 '20 at 3:05
  • Thanks! Big improvement to the answer, I've voted up! – Tom J Nowell Jul 2 '20 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.