Как передать внешние переменные в фильтры/действия WordPress
Мне нужно передать пользовательские данные в фильтр, предоставляемый сторонним плагином. Все способы, которые я видел, действительно сложные и трудные для понимания.
Возьмем этот пример:
$score = 42; //Какое-то сложное вычисление, которое я не хочу повторять.
function add_score_to_title($title) {
return 'Результаты теста (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
Как я могу передать переменную $score
в функцию add_score_to_title()
?
В итоге я решил добавить мою переменную в глобальный объект $wp
. В результате получается так:
global $wp;
$score = 42; //Какое-то сложное вычисление, которое я не хочу повторять.
$wp->some_random_name_for_score = $score;
function add_score_to_title($title) {
global $wp;
$score = $wp->some_random_name_for_score;
return 'Результаты теста (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
Грязное решение? Возможно. Простое? Да! Есть ли недостатки у этого метода? Давайте обсудим.
ОБНОВЛЕНИЕ Вот полный код -> http://pastebin.com/fkSXY04m

У вас есть как минимум два варианта:
- Глобализировать нужную переменную, а затем ссылаться на неё внутри callback-функции
- Обернуть логику расчёта оценки в функцию, а затем ссылаться на неё внутри callback-функции
Глобализация переменной
<?php
global $score;
$score = 42; //Какие-то сложные вычисления, которые я не хочу повторять.
function add_score_to_title($title) {
global $score;
return 'Результаты теста (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Обёртка логики расчёта оценки
Если вам нужен расчёт оценки только внутри фильтра, вынесите логику в сам callback:
<?php
function add_score_to_title($title) {
$score = 0;
$questions = get_quiz_result_questions();
$total_questions = 0;
foreach( $questions as $question ) {
$order = $question->order;
if( $order >= 100 ) {
break;
}
if( $question->correct == $_POST['Q'][$order] ) {
$score++;
}
$total_questions++;
return 'Результаты теста (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Ещё лучше — обернуть расчёт оценки в отдельную функцию и вызывать её внутри callback-функции:
<?php
function wpse48677_get_score() {
$score = 0;
$questions = get_quiz_result_questions();
$total_questions = 0;
foreach( $questions as $question ) {
$order = $question->order;
if( $order >= 100 ) {
break;
}
if( $question->correct == $_POST['Q'][$order] ) {
$score++;
}
$total_questions++;
$output['score'] = $score;
$output['total_questions'] = $total_questions;
return $output;
}
function add_score_to_title($title) {
$score_results = wpse48677_get_score();
$score = $score_results['score'];
return 'Результаты теста (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Если у вас возникают проблемы с доступом к объекту $_POST
, вы также можете зарегистрировать свою переменную запроса и использовать get_query_var()
для получения данных:
function add_score_query_vars( $query_vars ) {
$query_vars[] = 'Q';
return $query_vars;
}
add_filter( 'query_vars', 'add_score_query_vars' );
После этого $_POST['Q']
можно заменить на get_query_var('Q')
.

Это не имеет ничего общего с количеством аргументов, передаваемых в функцию через apply_filters...

Попробовал метод №1, который вы упомянули, с глобализацией переменной. Это не сработало. Параметр accepted args тоже не помогает, так как у меня нет контроля над тем, какие переменные передаются в функцию обратного вызова.

Извините, вы были правы. Я сначала вычислял $score
, а потом делал его глобальным. Неудивительно, что это не работало. Спасибо!

-1. Первый вариант выставляет переменную в глобальное состояние, второй вариант не работает...

Обновил, чтобы отразить, что я ошибался насчёт второго варианта. Но что плохого в использовании глобальной переменной? Это определённо лучше, чем использовать анонимную функцию в колбэке. :)

Нет, это не так. И вот несколько причин, почему следует избегать такой практики. Возможно, для его конкретного случая это нормально, но что, если кто-то действительно создаст публичный плагин, используя ваше решение?

"Глобальные переменные — это зло"? Серьёзно? Тогда весь код WordPress нужно переписать, поскольку он полагается на множество глобальных переменных.

Именно. Это одна из причин, почему базовая кодовая база WP — одна из худших в вебе. Впрочем, у WP есть оправдание — необходимость совместимости с плагинами, разработанными для старых версий. У вас такого оправдания нет :)

Да уж... Я пожалуй воздержусь от комментариев. Спасибо, что указали на проблему со вторым вариантом; я удалил его из вопроса. Я остаюсь при мнении, что использование глобальных переменных допустимо. Если бы я знал, как генерируется или возвращается вывод Плагина, я мог бы предложить лучшее решение.

Смотрите обновленный ответ; изучив Pastebin, я предложил два метода, которые предпочел бы использованию глобальной переменной.

Лучше, но он хочет, чтобы "score" был доступен позже в скрипте без повторного вычисления, поэтому использование "static" переменной здесь имеет смысл.

Использование функции для вычисления score: нельзя ли сохранить результат вычисления в кеше объекта?

осень 2015 года, а WP всё ещё полагается на зловредные глобальные переменные. Это оправдание уже слишком старое. И +1 за предложение использовать глобализацию. В моём случае колбэку требовался доступ к $user
, который мне пришлось сделать глобальным, так как мой вызов apply_filters
инициировался внутри функции.

Эти глобальные переменные зловредны, народ, успокойтесь. Мне очень нравится этот ответ. Автор вопроса спрашивал, как можно передавать переменные между функциями. В этом ответе объясняются несколько способов сделать это. Начинается с глобальных переменных, которых действительно лучше избегать, но они всё же существуют и решают поставленную задачу. Затем в ответе объясняются несколько более правильных вариантов, которые могут подойти в определённых случаях.

function add_score_to_title($title = false) {
static $score = false;
if($score === false){
// выполняем расчет
}
// вызов плагина (фильтр)
if($title !== false)
return 'Результаты теста (' . $score . ') - ' . $title;
// ваш вызов
return $score;
}
Вызовите эту функцию в любом месте вашего скрипта, чтобы получить оценку - она будет рассчитана только один раз.
Другой способ, с использованием анонимных функций:
// выполняем расчет
$score = 'xxx';
add_filter('aioseop_title_single', function($title) use($score){
return 'Результаты теста (' . $score . ') - ' . $title;
});

Анонимные функции не следует использовать в вызовах add_filter()
или add_action()
. Их нельзя удалить с помощью remove_function()
.

Вы имеете в виду remove_filter, который в основном используется для удаления встроенных фильтров, а не фильтров, добавленных плагинами/темами...

В следующем примере переменная $my_calculation
находится в глобальной области видимости, однако внутри нашей локальной функции нам необходимо объявить global $my_calculation
, чтобы получить доступ к этой переменной в глобальной области.
<?php
$my_calculation = 'результат!';
function my_function() {
global $my_calculation;
return $my_calculation;
}
add_filter( 'function_something_here', 'my_function');
?>
Это всего лишь один из подходов, и он выглядит достаточно аккуратным. Будет ли это работать в вашем случае?

На одно объявление "global" меньше. Посмотрите на его второй пример - он дважды объявляет global $wp!

Можете ли вы вывести результат с помощью print
или echo
, чтобы убедиться, что ваша функция действительно работает перед передачей её в фильтр?

Упс! Глобализация переменной $score
действительно работает. Я ошибся и сначала присвоил значение $score
, а потом сделал её глобальной, что, очевидно, не работает. Если сделать правильно — сначала объявить $score
глобальной, а затем присвоить значение — всё работает как ожидалось. Спасибо всем.
