Take the 2-minute tour ×
WordPress Development Stack Exchange 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
add comment

3 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
add comment

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.

Edit: years later

One thing I've seen a lot of plugins doing is to use something similar to the "Singleton" pattern. They create a getInstance() method to get the single instance of the class. This is probably the best solution I've seen. Example plugin:

class ExamplePlugin
{
    protected static $instance = NULL;

    public static function getInstance() {
        NULL === self::$instance and self::$instance = new self;
        return self::$instance;
    }
}

The first time getInstance() is called, it instantiates the class and saves its pointer. You can use that to hook in actions.

One problem with this is that you can't use getInstance() inside the constructor if you use such a thing. This is because the new calls the constructor before setting the $instance, so calling getInstance() from the constructor leads to an infinite loop and breaks everything.

One workaround is to not use the constructor (or, at least, not to use getInstance() within it), but to explicitly have an "init" function in the class to set up your actions and such. Like this:

public static function init() {
    add_action( 'wp_footer', array( ExamplePlugin::getInstance(), 'my_action' ) );
}

With something like this, at the end of the file, after the class has been all defined and such, instantiating the plugin becomes as simple as this:

ExamplePlugin::init();

Init starts to add your actions, and in so doing it calls getInstance(), which instantiates the class and makes sure only one of them exists. If you don't have an init function, you would do this to instantiate the class initially instead:

ExamplePlugin::getInstance();

To address the original question, removing that action hook from the outside (aka, in another plugin) can then be done like so:

remove_action( 'wp_footer', array( ExamplePlugin::getInstance(), 'my_action' ) );

Put that in something hooked to the plugins_loaded action hook and it'll undo the action being hooked by the original plugin.

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
    
+1 these instructions really helped me to remove a filter in a singleton pattern class. –  Devin Walker Mar 28 at 2:01
add comment

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
add comment

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.