Removing hook that uses function or static method as a callback is simple with remove_filter()
or remove_action()
functions because you know exactly what callback was attached to some hook.
It gets a little tricky when your callback is an object and that object is not made available so you can’t unhook it. Consider following, simple example.
Lets say that some plugin has the following code:
class MD_Unhookable_Class {
public function __construct() {
add_filter( 'the_content', array( $this, 'lorem_ipsum' ) );
add_filter( 'the_title', array( $this, 'lorem_ipsum' ) );
}
public function lorem_ipsum( $content ) {
return 'Lorem Ipsum';
}
}
new MD_Unhookable_Class;
What this does is that it replaces text of post titles and content with Lorem Ipsum string. Now you want to remove hook that changes titles and leave hook for content. Well, you can’t. Class instance that was attached to hook was not made available because it simply called new MD_Unhookable_Class;
without making its instance available.
There were some workarounds but changes in WP 4.7 will make this obsolete anyway.
Instead of instantiating class without storing its instance, lets store that instance, for example, in a global variable $md_hookable_class
:
class MD_Unhookable_Class {
public function __construct() {
add_filter( 'the_content', array( $this, 'lorem_ipsum' ) );
add_filter( 'the_title', array( $this, 'lorem_ipsum' ) );
}
public function lorem_ipsum( $content ) {
return 'Lorem Ipsum';
}
}
$GLOBALS['md_hookable_class'] = new MD_Unhookable_Class;
Now you only need to use this global to remove callback:
remove_filter( 'the_title', array( $GLOBALS['md_hookable_class'], 'lorem_ipsum') );
This is just example, you don’t have to use global variable to store class instance, you can have static method in a class that instantiate class and stores that instance, or you can instantiate class inside another class and store instance as class property.
Same thing applies to anonymous functions when used as callbacks, there is no way to unhook them.
When your code is public and you use PHP classes for development, I recommend that you make class instances available so that other developers using your plugin or theme can simply unhook things that they don’t need or need differently. Also, I recommend that you avoid anonymous functions in public code for the same reason.
If the class uses an internal instance variable you can use it to unhook the action.
I recently used this method in a class that had the following:
so I was able to use:
remove_action('woocommerce_email_before_order_table', array(TheClassName::instance(), 'email_display'));
to unhook it.
Well, the point of this post was that plugin authors should enable something like in your example. 🙂 Maybe I wasn’t clear, it doesn’t matter how you store instance, it is only important that it’s publicly accessible.
Personally, I use similar code, difference is that I store instance inside method’s static variable so there is no way that anyone change it:
I understand the aim of the post.
I was providing another example so that people would know how to unhook the function when they see an $instance variable in a class.
I’ve never seen $GLOBALS being used to instantiate a class.
$GLOBALS['md_hookable_class'] = new MD_Unhookable_Class;
is just a simpler form ofglobal $md_hookable_class; $md_hookable_class = new MD_Unhookable_Class;
. WordPress core uses it.I thought that providing simple, short example would make this more understandable.
I’m not arguing with you 🙂
Your example did make the concept more understandable. Thanks for the wp-settings.php link. Good to know.
I included some code to show another way that the instance can be made publicly accessible and how to unhook a function in that scenario.
No problem, I never had that impression, just that we don’t understand each other. 😉 Thanks for stopping by!