Как удалить фильтр, который является анонимным объектом?
В моем файле functions.php
я хотел бы удалить приведенный ниже фильтр, но я не уверен, как это сделать, так как он находится в классе. Как должен выглядеть remove_filter()
?
add_filter('comments_array',array( &$this, 'FbComments' ));
Это на линии 88 здесь.
3 answers
Это очень хороший вопрос. Это относится к темному сердцу API плагинов и лучшим методам программирования.
Для следующего ответа я создал простой плагин, чтобы проиллюстрировать проблему с помощью легко читаемого кода.
<?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;
}
Теперь мы видим следующее:
WordPress требуется имя для фильтра. Мы его не предоставили, поэтому WordPress звонит _wp_filter_build_unique_id()
и создает его. Это имя не предсказуемо, потому что оно использует spl_object_hash()
.
Если мы запустим var_export()
на $GLOBALS['wp_filter'][ 'wp_footer' ]
сейчас мы получаем что-то вроде этого:
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,
),
),
)
Чтобы найти и удалить наше злое действие, мы должны пройти через соответствующие фильтры для крючка (действие - это просто очень простой фильтр), проверить, является ли это массивом и является ли объект экземпляром класса. Затем мы берем приоритет и удаляем фильтр, , даже не видя реального идентификатора.
Хорошо, давайте поместим это в функция:
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
);
}
}
}
}
}
Когда мы вызываем эту функцию? Невозможно точно узнать, когда будет создан исходный объект. Может быть, иногда раньше 'plugins_loaded'
? Может быть, позже?
Мы используем точно такой же крючок, с которым связан объект, и переходим очень рано с приоритетом 0
. Это единственный способ быть по-настоящему уверенным. Вот как мы удалили бы этот метод 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'
);
}
Результат:
И это должно исключить действие из вашего вопроса (не проверено):
add_action( 'comments_array', 'kill_FbComments', 0 );
function kill_FbComments()
{
remove_anonymous_object_filter(
'comments_array',
'SEOFacebookComments',
'FbComments'
);
}
Заключение
- Всегда пишите предсказуемый код. Задайте понятные имена для ваших фильтров и действий. Облегчите снятие любого крючка.
- Создайте свой объект на предсказуемом действии, например на
'plugins_loaded'
. Не только когда ваш плагин вызывается WordPress.
Я не уверен, но вы можете попробовать использовать синглтон.
Вы должны сохранить ссылку на объект в статическом свойстве вашего класса, а затем вернуть эту статическую переменную из статического метода. Что-то вроде этого:
class MyClass{
private static $ref;
function MyClass(){
$ref = &$this;
}
public static function getReference(){
return self::$ref;
}
}
Пока вы знаете объект (и вы используете PHP 5.2 или выше - текущая стабильная версия PHP 5.5, 5.4 все еще поддерживается, 5.3 - конец жизни), вы можете просто удалить его с помощью метода remove_filter()
. Все, что вам нужно запомнить, это объект, имя метода и приоритет (если используется):
remove_filter('comment_array', [$this, 'FbComments']);
Однако вы допускаете небольшую ошибку в своем коде. Не добавляйте префикс $this
к амперсанду &
, который был необходим в PHP 4 (!), и это давно назрело. Это может помочь справиться с вашими крючками проблематично, так что просто оставьте это в стороне:
add_filter('comments_array', [$this, 'FbComments]));
И это все.