Tell me more ×
WordPress Answers is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

In a situation where a plugin has encapsulated its methods within a class and then registered a filter or action against one of those methods, how do you remove the action or the filter if you no longer have access to that class' instance?

For example, suppose you have a plugin that does this:

class MyClass {
    function __construct() {
       add_action( "plugins_loaded", array( $this, 'my_action' ) );
    }

    function my_action() {
       // do stuff...
    }
}

new MyClass();

Noting that I now have no way of accessing the instance, how do I unregister the class? This: remove_action( "plugins_loaded", array( MyClass, 'my_action' ) ); doesn't seem to be the right approach - at least, didn't seem to work in my case.

share|improve this question
@kaiser thanks for the edits – Tom Auger Dec 9 '11 at 20:20
N/P. Does below A work for you? – kaiser Dec 9 '11 at 21:20

4 Answers

up vote 4 down vote accepted

The best thing to do here is to use a static class. The following code should be instructional:

class MyClass {
    function __construct() {
        add_action( 'wp_footer', array( $this, 'my_action' ) );
    }
    function my_action() {
        print '<h1>' . __class__ . ' - ' . __function__ . '</h1>';
    }
}
new MyClass();


class MyStaticClass {
    public static function init() {
        add_action( 'wp_footer', array( __class__, 'my_action' ) );
    }
    public static function my_action() {
        print '<h1>' . __class__ . ' - ' . __function__ . '</h1>';
    }
}
MyStaticClass::init();

function my_wp_footer() {
    print '<h1>my_wp_footer()</h1>';
}
add_action( 'wp_footer', 'my_wp_footer' );

function mfields_test_remove_actions() {
    remove_action( 'wp_footer', 'my_wp_footer' );
    remove_action( 'wp_footer', array( 'MyClass', 'my_action' ), 10 );
    remove_action( 'wp_footer', array( 'MyStaticClass', 'my_action' ), 10 );
}
add_action( 'wp_head', 'mfields_test_remove_actions' );

If you run this code from a plugin you should notice that the method of the StaticClass as well as the function will removed from wp_footer.

share|improve this answer
Point taken, but not all classes can simply be converted to be static. – Geert Feb 29 '12 at 11:40
I accepted this answer because it answers the question most directly, though Otto's response is the best practice. I note here that I don't think you need to explicitly declare static. It's been my experience (though I could be wrong) that you can just treat the function as though it were static array( 'MyClass', 'member_function' ) and it often works without the 'static' keyword. – Tom Auger Apr 24 '12 at 17:40

Whenever a plugin creates a new MyClass();, it should assign it to a uniquely named variable. That way, the instance of the class is accessible.

So if he was doing $myclass = new MyClass();, then you could do this:

global $myclass;
remove_action( 'wp_footer', array( $myclass, 'my_action' ) );

This works because plugins are included in the global namespace, so implicit variable declarations in the main body of a plugin are global variables.

If the plugin doesn't save the identifier of the new class somewhere, then technically, that's a bug. One of the general principles of Object Oriented Programming is that objects which are not being referenced by some variable somewhere are subject to cleanup or elimination.

Now, PHP in particular doesn't do this like Java would, because PHP is sorta a half-arsed OOP implementation. The instance variables are just strings with unique object names in them, sort of thing. They only work because of the way the variable function name interaction works with the -> operator. So just doing new class() can indeed work perfectly, just stupidly. :)

So, bottom line, never do new class();. Do $var = new class(); and make that $var accessible in some way for other bits to reference it.

share|improve this answer
1  
+1 Tru dat. This is clearly a best practice. We should all endeavour to write our plugin code that way. – Tom Auger Apr 24 '12 at 17:41

Honestly, I don't know the answer, but the work-around:

function remove_filter_test() 
{
    global $wp_filter;
    $filters = $wp_filter['plugins_loaded'][ 10 ];

    // Inspect state before removal:
    echo '<pre>'; print_r( $filters ); echo '<pre>';

    foreach( $filters as $name => $f )
    {
        if ( strstr( $name, 'my_action' ) )
            unset( $filters[ $name ] ); 
    }

    // Inspect result:
    echo '<pre>'; print_r( $filters ); echo '<pre>';
}
add_action( 'admin_footer', 'remove_filter_test' );

Point is that remove_action doesn't do anything different. Point also is that the function name gets prefixed with a long number and whatever combination in case of hooking from inside a class. So the best guess is to search for the occurence of the function name in the array key of the needed priority.


EDIT

Maybe using a reference works:

function remove_filter_test2() 
{
    global $wp_filter;
    $filters = $wp_filter['plugins_loaded'][ 10 ];

    // Inspect state before removal:
    echo '<pre>'; print_r( $filters ); echo '<pre>';

    // It could be that you have to add the class to the global scope during your init or __construct() inside the class:
    # $class = __CLASS__;
    # if ( empty( $GLOBALS[ class ] ) )
    #    $GLOBALS[ $class ] = new $class;
    # global $class;
    $class = new MyClass(); // or just call it
    remove_action( 'plugins_loaded', array( &$class, 'my_action' ) );

    // Inspect result:
    echo '<pre>'; print_r( $filters ); echo '<pre>';
}
add_action( 'admin_footer', 'remove_filter_test2' );
share|improve this answer

2 small PHP functions for allow removing filter/action with "anonymous" class : https://github.com/herewithme/wp-filters-extras/

share|improve this answer
Very cool functions. Thanks for posting that here! – Tom Auger Nov 6 '12 at 14:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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