In my functions.php
file I would like to remove the below filter, but I'm not sure how to do it since it's in a class. What should remove_filter()
look like?
add_filter('comments_array',array( $this, 'FbComments' ));
It's on line 88 here.
In my functions.php
file I would like to remove the below filter, but I'm not sure how to do it since it's in a class. What should remove_filter()
look like?
add_filter('comments_array',array( $this, 'FbComments' ));
It's on line 88 here.
That’s a very good question. It goes down to the dark heart of the plugin API and best programming practices.
For the following answer I created a simple plugin to illustrate the problem with easy to read code.
<?php # -*- coding: utf-8 -*-
/* Plugin Name: Anonymous OOP Action */
if ( ! class_exists( 'Anonymous_Object' ) )
{
/**
* Add some actions with randomized global identifiers.
*/
class Anonymous_Object
{
public function __construct()
{
add_action( 'wp_footer', array ( $this, 'print_message_1' ), 5 );
add_action( 'wp_footer', array ( $this, 'print_message_2' ), 5 );
add_action( 'wp_footer', array ( $this, 'print_message_3' ), 12 );
}
public function print_message_1()
{
print '<p>Kill me!</p>';
}
public function print_message_2()
{
print '<p>Me too!</p>';
}
public function print_message_3()
{
print '<p>Aaaand me!</p>';
}
}
// Good luck finding me!
new Anonymous_Object;
}
Now we see this:
WordPress needs a name for the filter. We didn’t provide one, so WordPress calls _wp_filter_build_unique_id()
and creates one. This name is not predictable because it uses spl_object_hash()
.
If we run a var_export()
on $GLOBALS['wp_filter'][ 'wp_footer' ]
we get something like this now:
array (
5 =>
array (
'000000002296220e0000000013735e2bprint_message_1' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_1',
),
'accepted_args' => 1,
),
'000000002296220e0000000013735e2bprint_message_2' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_2',
),
'accepted_args' => 1,
),
),
12 =>
array (
'000000002296220e0000000013735e2bprint_message_3' =>
array (
'function' =>
array (
0 =>
Anonymous_Object::__set_state(array(
)),
1 => 'print_message_3',
),
'accepted_args' => 1,
),
),
20 =>
array (
'wp_print_footer_scripts' =>
array (
'function' => 'wp_print_footer_scripts',
'accepted_args' => 1,
),
),
1000 =>
array (
'wp_admin_bar_render' =>
array (
'function' => 'wp_admin_bar_render',
'accepted_args' => 1,
),
),
)
To find and remove our evil action we have to go through the associated filters for the hook (an action is just a very simple filter), check if it is an array and if the object is an instance of the class. Then we take the priority and remove the filter, without ever seeing the real identifier.
Okay, let’s put that into a function:
if ( ! function_exists( 'remove_anonymous_object_filter' ) )
{
/**
* Remove an anonymous object filter.
*
* @param string $tag Hook name.
* @param string $class Class name
* @param string $method Method name
* @return void
*/
function remove_anonymous_object_filter( $tag, $class, $method )
{
$filters = $GLOBALS['wp_filter'][ $tag ];
if ( empty ( $filters ) )
{
return;
}
foreach ( $filters as $priority => $filter )
{
foreach ( $filter as $identifier => $function )
{
if ( is_array( $function)
and is_a( $function['function'][0], $class )
and $method === $function['function'][1]
)
{
remove_filter(
$tag,
array ( $function['function'][0], $method ),
$priority
);
}
}
}
}
}
When do we call this function? There is no way to know for sure when the original object is created. Maybe sometimes before 'plugins_loaded'
? Maybe later?
We use just the same hook the object is associated to and jump in very early with priority 0
. That’s the only way to be really sure. Here is how we would remove the method print_message_3()
:
add_action( 'wp_footer', 'kill_anonymous_example', 0 );
function kill_anonymous_example()
{
remove_anonymous_object_filter(
'wp_footer',
'Anonymous_Object',
'print_message_3'
);
}
Result:
And that should remove the action from your question (not tested):
add_action( 'comments_array', 'kill_FbComments', 0 );
function kill_FbComments()
{
remove_anonymous_object_filter(
'comments_array',
'SEOFacebookComments',
'FbComments'
);
}
'plugins_loaded'
. Not just when your plugin is called by WordPress.plugins_loaded
is called, which is exactly what plugins_loaded
is for. Of course, the class instance still needs to be accessible, possibly via singleton pattern.
As long as you know the object (and you use PHP 5.2 or higher - current stable PHP version is 5.5, 5.4 is still supported, 5.3 is end of life), you can just remove it with the remove_filter()
method. All you need to remember is the object, the method-name and the priority (if used):
remove_filter('comment_array', [$this, 'FbComments']);
However you do a little mistake in your code. Don't prefix $this
with the ampersand &
, that was needed in PHP 4 (!) and it's long-time overdue. This can render dealing with your hooks problematic, so just leave it out of the way:
add_filter('comments_array', [$this, 'FbComments]));
And that's it.
$this
from the outside (another plugin/theme).
$this
should be clear which object that is (and you probably just have a plugin/module class that always has $this
so can manage itself).
I'm not sure but you can try using a singleton.
You must store the object reference in a static property of your class and then return that static variable from a static method. Something like this:
class MyClass{
private static $ref;
function MyClass(){
$ref = $this;
}
public static function getReference(){
return self::$ref;
}
}
&
from your&$this
, it's a PHP 4 thing