Как передать внешние переменные в фильтры/действия 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...
onetrickpony
Попробовал метод №1, который вы упомянули, с глобализацией переменной. Это не сработало. Параметр accepted args тоже не помогает, так как у меня нет контроля над тем, какие переменные передаются в функцию обратного вызова.
kingkool68
Извините, вы были правы. Я сначала вычислял $score, а потом делал его глобальным. Неудивительно, что это не работало. Спасибо!
kingkool68
-1. Первый вариант выставляет переменную в глобальное состояние, второй вариант не работает...
onetrickpony
Обновил, чтобы отразить, что я ошибался насчёт второго варианта. Но что плохого в использовании глобальной переменной? Это определённо лучше, чем использовать анонимную функцию в колбэке. :)
Chip Bennett
Нет, это не так. И вот несколько причин, почему следует избегать такой практики. Возможно, для его конкретного случая это нормально, но что, если кто-то действительно создаст публичный плагин, используя ваше решение?
onetrickpony
"Глобальные переменные — это зло"? Серьёзно? Тогда весь код WordPress нужно переписать, поскольку он полагается на множество глобальных переменных.
Chip Bennett
Именно. Это одна из причин, почему базовая кодовая база WP — одна из худших в вебе. Впрочем, у WP есть оправдание — необходимость совместимости с плагинами, разработанными для старых версий. У вас такого оправдания нет :)
onetrickpony
Да уж... Я пожалуй воздержусь от комментариев. Спасибо, что указали на проблему со вторым вариантом; я удалил его из вопроса. Я остаюсь при мнении, что использование глобальных переменных допустимо. Если бы я знал, как генерируется или возвращается вывод Плагина, я мог бы предложить лучшее решение.
Chip Bennett
Смотрите обновленный ответ; изучив Pastebin, я предложил два метода, которые предпочел бы использованию глобальной переменной.
Chip Bennett
Лучше, но он хочет, чтобы "score" был доступен позже в скрипте без повторного вычисления, поэтому использование "static" переменной здесь имеет смысл.
onetrickpony
Использование функции для вычисления score: нельзя ли сохранить результат вычисления в кеше объекта?
Stephen Harris
осень 2015 года, а WP всё ещё полагается на зловредные глобальные переменные. Это оправдание уже слишком старое. И +1 за предложение использовать глобализацию. В моём случае колбэку требовался доступ к $user, который мне пришлось сделать глобальным, так как мой вызов apply_filters инициировался внутри функции.
Ejaz
Эти глобальные переменные зловредны, народ, успокойтесь. Мне очень нравится этот ответ. Автор вопроса спрашивал, как можно передавать переменные между функциями. В этом ответе объясняются несколько способов сделать это. Начинается с глобальных переменных, которых действительно лучше избегать, но они всё же существуют и решают поставленную задачу. Затем в ответе объясняются несколько более правильных вариантов, которые могут подойти в определённых случаях.
squarecandy
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().
Chip Bennett
Вы имеете в виду remove_filter, который в основном используется для удаления встроенных фильтров, а не фильтров, добавленных плагинами/темами...
onetrickpony
В следующем примере переменная $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!
Adam
Можете ли вы вывести результат с помощью print или echo, чтобы убедиться, что ваша функция действительно работает перед передачей её в фильтр?
Adam
Упс! Глобализация переменной $score действительно работает. Я ошибся и сначала присвоил значение $score, а потом сделал её глобальной, что, очевидно, не работает. Если сделать правильно — сначала объявить $score глобальной, а затем присвоить значение — всё работает как ожидалось. Спасибо всем.
kingkool68