Как передать внешние переменные в фильтры/действия WordPress

11 апр. 2012 г., 22:37:25
Просмотры: 25.3K
Голосов: 12

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

Возьмем этот пример:

$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

5
Комментарии

Просто выполните свои "сумасшедшие вычисления" внутри этой функции...

onetrickpony onetrickpony
11 апр. 2012 г. 22:45:42

Вероятно, мой упрощённый пример сложно понять. Переменная $score используется во всём этом шаблоне. Я хочу вычислить её один раз и передать это значение в функцию, которая является колбэком для фильтра изменения заголовка страницы. Этот фильтр используется в плагине All In One SEO Pack. Многократное выполнение вычислений нежелательно.

kingkool68 kingkool68
11 апр. 2012 г. 22:54:33

Тогда вам следует показать больше кода, если вы хотите получить решение, которое не подразумевает введение глобального состояния (которое никогда не должно быть необходимо).

onetrickpony onetrickpony
11 апр. 2012 г. 23:00:03

Вот полный код в файле шаблона. $score вычисляется путем перебора массива элементов и инкрементации переменной.

http://pastebin.com/fkSXY04m

kingkool68 kingkool68
11 апр. 2012 г. 23:07:43

возможный дубликат Передача параметра в функции фильтров и действий

T.Todua T.Todua
14 мая 2015 г. 12:20:32
Все ответы на вопрос 3
14
12

У вас есть как минимум два варианта:

  1. Глобализировать нужную переменную, а затем ссылаться на неё внутри callback-функции
  2. Обернуть логику расчёта оценки в функцию, а затем ссылаться на неё внутри 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').

11 апр. 2012 г. 22:44:34
Комментарии

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

onetrickpony onetrickpony
11 апр. 2012 г. 22:46:40

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

kingkool68 kingkool68
11 апр. 2012 г. 22:55:58

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

kingkool68 kingkool68
11 апр. 2012 г. 23:11:35

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

onetrickpony onetrickpony
11 апр. 2012 г. 23:28:23

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

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:29:57

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

onetrickpony onetrickpony
11 апр. 2012 г. 23:34:10

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

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:37:14

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

onetrickpony onetrickpony
11 апр. 2012 г. 23:38:44

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

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:44:32

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

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:52:06

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

onetrickpony onetrickpony
11 апр. 2012 г. 23:57:55

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

Stephen Harris Stephen Harris
12 апр. 2012 г. 00:48:47

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

Ejaz Ejaz
7 окт. 2015 г. 20:56:50

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

squarecandy squarecandy
21 сент. 2017 г. 18:25:43
Показать остальные 9 комментариев
3
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;  
});
11 апр. 2012 г. 23:02:10
Комментарии

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

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:31:07

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

onetrickpony onetrickpony
11 апр. 2012 г. 23:36:03

Да; извините: remove_filter() и remove_action(). И нет причин, по которым фильтры плагинов/тем не могли бы быть разумно удалены: переопределение подключения таблиц стилей плагинов, дочерние темы и т.д.

Chip Bennett Chip Bennett
11 апр. 2012 г. 23:38:23
6

В следующем примере переменная $my_calculation находится в глобальной области видимости, однако внутри нашей локальной функции нам необходимо объявить global $my_calculation, чтобы получить доступ к этой переменной в глобальной области.

<?php 

    $my_calculation = 'результат!';

    function my_function() {

        global $my_calculation;
        return $my_calculation;

    }

    add_filter( 'function_something_here', 'my_function');   

?>

Это всего лишь один из подходов, и он выглядит достаточно аккуратным. Будет ли это работать в вашем случае?

11 апр. 2012 г. 22:57:06
Комментарии

Чем это отличается от того, что у него уже есть?

onetrickpony onetrickpony
11 апр. 2012 г. 23:00:22

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

Adam Adam
11 апр. 2012 г. 23:02:47

Это также не работает. Это первое, что я попробовал.

kingkool68 kingkool68
11 апр. 2012 г. 23:02:54

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

Adam Adam
11 апр. 2012 г. 23:04:27

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

kingkool68 kingkool68
11 апр. 2012 г. 23:12:57

Я как раз хотел это сказать после просмотра вашего скрипта — немного наоборот получилось ;) Удачи!

Adam Adam
11 апр. 2012 г. 23:14:01
Показать остальные 1 комментариев