Передача переменных по ссылке в 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.
Передача объекта в фильтр или событие по ссылке
Объекты всегда передаются по ссылке. Поэтому если в фильтре мы указываем объект, то не обязательно что-то придумывать, можно использовать стандартные функции хуков: apply_actions() или 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] => Привет Мир! ) */