Передача переменных по ссылке в apply_filters(), do_actions()
В этой заметке показано, как передать в фильтр переменную по ссылке. Такое бывает нужно крайне редко, но иногда все же нужно.
Суть проблемы.
Фильтр в WordPress изменяет передаваемое значение: изменяет значение переданной переменной и возвращает его в контекст, откуда был вызван фильтр. Но делает он это только с первой переменной, а остальные, дополнительные переменные просто передаются в фильтр и могут быть там использованы для всяких целей, но изменить их, так чтобы изменение повлияло на ту переменную которая передавалась мы никак не можем.
Например в PHP мы можем сделать так:
function func( $string, & $number ){
$number = 2;
return $string . ' апельсин';
}
$num = 1;
$str = 'сумасшедший';
$str = func( $str, $num );
echo $str; //> сумасшедший апельсин
echo $num; //> 2
Как видно, мы повлияли на переменную $num из функции. Это и есть передача переменной по ссылке из текущего контекста в контекст функции.
Но в фильтрах WordPress мы так не получится:
// функция для фильтра
function func( $string, & $number ){
$number = 2;
return $string . ' апельсин';
}
// подключаем функцию к фильтру
add_filter( 'myfilter', 'func', 0, 2 );
// вызываем фильтр
$num = 1;
$str = 'сумасшедший';
// оба вызова фильтра завершатся ошибкой PHP
$str = apply_filters('myfilter', $str, $num ); //> ошибка: функция ждет ссылку
$str = apply_filters('myfilter', $str, & $num ); //> ошибка: неожиданный символ &
Передача переменной по ссылке в фильтрах WordPress
В качестве решения данной задачи, можно использовать глобальную переменную, которая будет видна из любого контекста. Но это плохое решение, потому что использование глобальной переменной может повлиять на работу других процессов и вызывать конфликты. Костыли конечно наше все, но только когда без них никак.
Вариант 1
Нужно передать в первом параметре фильтра, сразу две переменные, так мы сможем влиять на обе сразу, а не только на первую. Передать две переменные можно сунув их в массив и передав этот массив. Выглядит это так:
// функция для фильтра
function func( $array ){
list( $string, $number ) = $array;
$string = $string . ' апельсин';
$number = 2;
return array( $string, $number );
}
// подключаем функцию к фильтру
add_filter( 'myfilter', 'func' );
// вызываем фильтр
$num = 1;
$str = 'сумасшедший';
list( $str, $num ) = apply_filters( 'myfilter', [ $str, $num ] );
// смотрим что получилось
echo $str; //> сумасшедший апельсин
echo $num; //> 2
Вариант 2
Текущую проблему также можно решить, используя специальную для этого функцию apply_filters_ref_array()
Передача переменной по ссылке в событиях WordPress
Ко всему выше сказанному нужно добавить, что в событиях WordPress есть возможность передавать переменные по ссылке. Для этого есть специальная функция событий do_action_ref_array()
// цепляем хук
add_action( 'myhook', 'myhook_func' );
function myhook_func( & $num ){
$num = 2; // изменяем переменную по ссылке
}
$num = 1;
// обрабатываем хук
do_action_ref_array('myhook', array( & $num ) );
echo $num; //> 2
А в заключении, предлагаю ознакомиться со всеми функциями хуков WordPress.
Передача объекта в фильтр или событие по ссылке
Объекты всегда передаются по ссылке. Поэтому если в фильтре мы указываем объект, то не обязательно что-то придумывать, можно использовать стандартные функции хуков: do_action() или apply_filters().
class MyClass {
public $var;
function __construct(){
$this->var = 'Привет';
}
}
add_filter( 'myfilter', function( $MyClass ){
$MyClass->var .= ' Мир!';
return $MyClass;
} );
$MyClass = new MyClass();
$MyClass = apply_filters( 'myfilter', $MyClass );
print_r( $MyClass );
/*
MyClass Object (
[var] => Привет Мир!
)
*/