10

That's a freaking bad practice I must say. Spent last two hours finding a solution to remove actions and filters added via Anonymous functions.

This is the code used on a Parent Theme, and I need to remove it.

/**
 * Add custom columns to admin comments grid
 *  * Rate that user set.
 */
add_filter( 'manage_edit-comments_columns', function( $default ) {
    $columns['smr_comment_rate']  = __( 'Rate', 'txtdmn' );

    return array_slice( $default, 0, 3, true ) + $columns + array_slice( $default, 2, NULL, true );
});

Got toscho's answer, played with it heavily, but no help. So, is there any other alternative that will remove actions/filters added via anonymous functions?

Thanks

10

The problem is that you can't distinguish form an anonymous function and another, so yes, it is possible to remove a closure (i.e. anonymous function) but if more than one closure act on same filter at same priority you have to make a choice, remove them all, ore remove only one (without knowing exactly which).

I'll show how to remove them all using a function highly derived from the one in the @toscho answer you posted:

/**
 * Remove an object filter.
 *
 * @param  string $tag                Hook name.
 * @param  string $class              Class name. Use 'Closure' for anonymous functions.
 * @param  string|void $method        Method name. Leave empty for anonymous functions.
 * @param  string|int|void $priority  Priority
 * @return void
 */
function remove_object_filter( $tag, $class, $method = NULL, $priority = NULL ) {
  $filters = $GLOBALS['wp_filter'][ $tag ];
  if ( empty ( $filters ) ) {
    return;
  }
  foreach ( $filters as $p => $filter ) {
    if ( ! is_null($priority) && ( (int) $priority !== (int) $p ) ) continue;
    $remove = FALSE;
    foreach ( $filter as $identifier => $function ) {
      $function = $function['function'];
      if (
        is_array( $function )
        && (
          is_a( $function[0], $class )
          || ( is_array( $function ) && $function[0] === $class )
        )
      ) {
        $remove = ( $method && ( $method === $function[1] ) );
      } elseif ( $function instanceof Closure && $class === 'Closure' ) {
        $remove = TRUE;
      }
      if ( $remove ) {
        unset( $GLOBALS['wp_filter'][$tag][$p][$identifier] );
      }
    }
  }
}

I've renamed the function remove_object_filter because it can remove all types of object filters: static class methods, dynamic object methods and closures.

The $priority argument is optional, but when removing closures it should be always used, otherwise the function will remove any closure added to the filter, no matter at which priority, because when $priority is omitted, all the filters using the target class/method or closure are removed.

How to use

// remove a static method
remove_object_filter( 'a_filter_hook', 'AClass', 'a_static_method', 10 );

// remove a dynamic method
remove_object_filter( 'a_filter_hook', 'AClass', 'a_dynamic_method', 10 );

// remove a closure
remove_object_filter( 'a_filter_hook', 'Closure', NULL, 10 );
  • I've tried this one and many others, but it simply does not work – adamj Jul 7 '17 at 3:25
  • @adamj since version 4.7, WordPress introduced new way to handle hooks, so this don't work anymore... – gmazzap Jul 7 '17 at 10:05
  • do you know of any alternatives by any chance? – adamj Jul 10 '17 at 7:27
  • 1
    @adamj I could update this for 4.7+, but no time now, and not sure when I'll have. It is fine to open a new question, where you link to this Q/A and say this is outdated, that way anyone can answer, so if I will have no time, maybe someone else will. As alternative, you could put a bounty on this Q, explaining that most upvoted answer here don't work in current version of WP... – gmazzap Jul 10 '17 at 10:28
3

What if you add your filter, with the priority 11, so it goes after? That's ugly, but might work in your case.

add_filter( 'manage_edit-comments_columns', function( $default ) {
    unset( $default['smr_comment_rate'] );

    return $default;
}, 11, 1 );
2

Anonymous filters and actions can be removed natively using the following:

remove_filter( $tag, function(){}, $priority )

When generating the unique id using spl_object_hash(), anonymous functions are comparable to one another, so the full closure object doesn't need to be re-created again.

If multiple filters or actions are connected to the same tag with the same priority, then it'll remove the latest filter or action which was added. If there's one you need to keep, you would have to remove all the filters up to the one you need removed, then re-add the others as necessary.

// Filter which was added and needs to be removed
add_filter( 'manage_edit-comments_columns', function( $default ) {
    $columns['smr_comment_rate']  = __( 'Rate', 'txtdmn' );

    return array_slice( $default, 0, 3, true ) + $columns + array_slice( $default, 2, NULL, true );
} );

// Removes the last anonymous filter to be added
remove_filter( 'manage_edit-comments_columns', function(){} );

This will generally come back to best practices. I'll only ever use anonymous functions as part of a custom theme I'm developing for a client, where I don't want the filter to be overwritten or removed. In any public theme or plugin I develop, I'll use a factory to initialise a class, add all my filters and actions, then store the instance as a static variable.

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.