Передача параметра в функции filter и action

17 мар. 2012 г., 13:24:53
Просмотры: 56.4K
Голосов: 64

Существует способ передать собственные параметры в функцию через add_filter или add_action. Рассмотрим следующий код:

function my_content($content, $my_param)
{
// что-то делаем...
// используем $my_param здесь ...
return $content;
}
add_filter('the_content', 'my_content', 10, 1);

Можно ли передать свой параметр? Например так:

add_filter('the_content', 'my_content($my_param)', 10, 1)

или

add_filter('the_content', 'my_content', 10, 1, $my_param)
1
Комментарии

вы можете использовать $_SESSION для хранения и получения параметров.

Sunil Kumar Sunil Kumar
10 июл. 2020 г. 12:23:15
Все ответы на вопрос 12
5
98

По умолчанию это невозможно. Есть обходные пути, если использовать ООП подход.
Вы можете создать класс для хранения значений, которые хотите использовать позже.

Пример:

/**
 * Хранит значение и вызывает любую существующую функцию с этим значением.
 */
class WPSE_Filter_Storage
{
    /**
     * Заполняется через __construct(). Используется в __call().
     *
     * @type mixed Любой нужный вам тип.
     */
    private $values;

    /**
     * Сохраняет значения для последующего использования.
     *
     * @param  mixed $values
     */
    public function __construct( $values )
    {
        $this->values = $values;
    }

    /**
     * Перехватывает все вызовы функций, кроме __construct().
     *
     * Внимание: Даже если функция вызывается с одной строкой в качестве
     * аргумента, она будет передана как массив.
     *
     * @param  string $callback Имя функции
     * @param  array  $arguments
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function __call( $callback, $arguments )
    {
        if ( is_callable( $callback ) )
            return call_user_func( $callback, $arguments, $this->values );

        // Вызвана неверная функция.
        throw new InvalidArgumentException(
            sprintf( 'Файл: %1$s<br>Строка %2$d<br>Не вызывается: %3$s',
                __FILE__, __LINE__, print_r( $callback, TRUE )
            )
        );
    }
}

Теперь вы можете вызывать класс с любой функцией — если функция существует где-то, она будет вызвана с вашими сохраненными параметрами.

Создадим демонстрационную функцию …

/**
 * Функция фильтра.
 * @param  array $content
 * @param  array $numbers
 * @return string
 */
function wpse_45901_add_numbers( $args, $numbers )
{
    $content = $args[0];
    return $content . '<p>' . implode( ', ', $numbers ) . '</p>';
}

… используем её один раз …

add_filter(
    'the_content',
    array (
        new WPSE_Filter_Storage( array ( 1, 3, 5 ) ),
        'wpse_45901_add_numbers'
    )
);

… и ещё раз …

add_filter(
    'the_content',
    array (
        new WPSE_Filter_Storage( array ( 2, 4, 6 ) ),
        'wpse_45901_add_numbers'
    )
);

Вывод:

Пример вывода

Ключевой момент — повторное использование: вы можете повторно использовать класс (и в наших примерах также функцию).

PHP 5.3+

Если вы используете PHP версии 5.3 или новее, замыкания сделают это намного проще:

$param1 = '<p>Это работает!</p>';
$param2 = 'Это тоже работает!';

add_action( 'wp_footer', function() use ( $param1 ) {
        echo $param1;
    }, 11 
);
add_filter( 'the_content', function( $content ) use ( $param2 ) {
        return t5_param_test( $content, $param2 );
    }, 12
);

/**
 * Добавляет строку к содержимому поста
 *
 * @param  string $content
 * @param  string $string В нашем примере это $param2.
 * @return string
 */
function t5_param_test( $content, $string )
{
    return "$content <p><b>$string</b></p>";
}

Недостаток в том, что вы не можете писать unit-тесты для замыканий.

17 мар. 2012 г. 20:32:12
Комментарии

Вы не только получаете голос за качественный ответ на проблему, которая должна иметь встроенное решение в ядре WP, но и за то, что вернулись через пять месяцев, чтобы обновить свой ответ примером с замыканием (closure) для PHP 5.3+.

Adam Adam
17 нояб. 2013 г. 09:17:38

Отличный ответ! Но как я могу удалить этот фильтр, созданный анонимной функцией, позже?

Vinicius Tavares Vinicius Tavares
12 авг. 2014 г. 06:33:54

@ViniciusTavares Не можете. Подумайте, прежде чем использовать это. :)

fuxia fuxia
12 авг. 2014 г. 11:17:41

Однако обратите внимание, что если сохранить анонимную функцию в переменную (например, $func = function() use ( $param1 ) { $param1; }; и add_action( $func, 11);), то её можно будет удалить с помощью remove_action( $func, 11 );

bonger bonger
2 мая 2015 г. 21:49:18

Но не рекомендуется использовать анонимные функции в плагинах или темах, которые вы выпускаете в мир (в своих проектах их использовать можно). Проблема в том, что их потом нельзя будет отключить. Любой выбранный вами подход должен позволять отключать функции в будущем.

Mueyiwa Moses Ikomi Mueyiwa Moses Ikomi
22 февр. 2018 г. 18:04:32
0

Используйте анонимные функции в PHP:

$my_param = 'my theme name';
add_filter('the_content', function ($content) use ($my_param) {
    //Теперь $my_param доступен внутри функции
    if (is_page()) {
        $content = $my_param . ':<br>' . $content;
    }
    return $content;
}, 10, 1);
23 янв. 2016 г. 12:26:32
0

Правильный, действительно короткий и наиболее эффективный способ передачи любого количества аргументов в фильтры и действия WordPress от @Wesam Alalem можно найти здесь, где используется замыкание (closure).

Я бы только добавил, что можно сделать код еще более понятным и гибким, отделив основной рабочий метод от анонимного замыкания. Для этого просто вызовите метод из замыкания, как показано ниже (модифицированный пример из ответа @Wesam Alalem).

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

// ... внутри какого-либо класса

private function myMethod() {
    $my_param = 'название моей темы';
    add_filter('the_content', function ($content) use ($my_param) {
        // Это анонимное замыкание, которое позволяет передавать 
        // любое количество параметров через ключевое слово 'use'.
        // Это всего лишь однострочник.
        // $my_param теперь доступен через ключевое слово 'use' выше
        return $this->doThings($content, $my_param);
    }, 10, 2);
}

private function doThings($content, $my_param) {
    // Здесь можно вызвать другой метод для выполнения дополнительных действий,
    // какой бы сложной ни была ваша логика.
    $morethings = '';
    if ($content = 'еще что-то') {
        $morethings = (new MoreClass())->get();
    }
    return $my_param . ':<br>' . $content . $morethings;
}
14 окт. 2018 г. 19:10:58
0

Создайте функцию с необходимыми аргументами, которая возвращает другую функцию. Передайте эту функцию (анонимную функцию, также известную как замыкание) в хук WordPress.

Пример показан для административного уведомления в бэкенде WordPress.

public function admin_notice_func( $message = '')
{
$class = 'error';
    $output = sprintf('<div class="%s"><p>%s</p></div>',$class, $message);
    $func = function() use($output) { print $output; };
    return $func;
}
$func = admin_notice_func('Сообщение');
add_action('admin_notices', $func);
20 янв. 2016 г. 20:53:14
0

Как упоминалось в других ответах, передача параметра в функцию обратного вызова по умолчанию невозможна. ООП и анонимные функции в PHP — это обходные пути, НО:

  1. Ваш код может не быть ООП
  2. Вам может понадобиться удалить этот фильтр впоследствии

Если это ваш случай, есть ещё один обходной путь, который вы можете использовать: воспользуйтесь функциями add_filter и apply_filters, чтобы сделать нужный параметр доступным в функции обратного вызова:

// Обходной путь для "сохранения" параметра, который нужно передать в функцию обратного вызова.
add_filter( 'pass_param', function() use ( $param ){ return $param; } );

// Подключите вашу функцию к фильтру, который нужно обработать.
add_filter( 'actual_filter', 'myCallback' );

// Ваша функция обратного вызова, которая фактически фильтрует данные.
function myCallback()
{
   // Получаем параметр, который не смогли передать напрямую в эту функцию.
   $param = apply_filters( 'pass_param', '' );

   // Делаем что-то с параметром, переданным через обходной путь, чтобы использовать его для фильтрации.
   return $param;
}
7 мая 2020 г. 12:12:35
3

Вы всегда можете использовать глобальные переменные..

  global $my_param;
25 авг. 2017 г. 02:47:08
Комментарии

Это не является ответом на вопрос. Когда у вас будет достаточно репутации, вы сможете оставлять комментарии к любым сообщениям; вместо этого предоставляйте ответы, не требующие уточнений от автора вопроса. - Из обзора

cjbj cjbj
25 авг. 2017 г. 07:25:56

@cjbj На самом деле является. Вопрос в том, можно ли передавать параметры в "функцию", которая находится в add_filter или add_action. Не было ясно, хотел ли пользователь передать их в саму функцию add_filter или add_action, хотя это и подразумевается. :)

samjco-com samjco-com
25 авг. 2017 г. 08:17:43

Это не игра "Jeopardy". Ответ в форме вопроса больше подходит для комментариев, а не для ответов. Думаю, именно это имел в виду cjbj.

Jake Jake
9 дек. 2020 г. 18:40:25
0

Несмотря на возможность прямого вызова функции, сделайте это более элегантным способом: передайте анонимную функцию в качестве callback.

Например:

У меня есть единая функция для перевода заголовка, содержимого и отрывка из моих записей. Поэтому мне нужно передать в эту основную функцию несколько аргументов, указывающих, кто вызывает.

add_filter( 'the_title', function( $text ) { 
    return translate_text( $text, 'title', 'pl' );
});

add_filter( 'the_content', function( $text ) { 
    return translate_text( $text, 'content', 'pl' );
});

add_filter( 'the_excerpt', function( $text ) { 
    return translate_text( $text, 'excerpt', 'pl' );
});

Таким образом, основная функция translate_text получает столько параметров, сколько я хочу, просто потому что я передал анонимную функцию в качестве callback.

25 окт. 2019 г. 23:30:52
1

Я согласен, что ответ fuxia выше предлагает предпочтительные подходы. Но пока я пытался разобраться с ООП-решением, я наткнулся на способ, который устанавливает, а затем снимает и фильтр, и глобальную переменную:

function my_function() {
    
    // Объявляем глобальную переменную и устанавливаем её значение
    global $my_global;
    $my_global = 'что-то';
        
    // Добавляем фильтр
    add_filter( 'some_filter', 'my_filter_function' );
    
    // Делаем то, для чего нам нужен был фильтр
    echo $filtered_stuff; 
    
    // Удаляем фильтр (чтобы он не повлиял на что-то ещё, что выполнится позже)
    remove_filter( 'some_filter', 'my_filter_function' );

    // Удаляем глобальную переменную (потому что нам не нравятся глобальные переменные в коде)
    my_unset_function( 'my_global' );
    
}

function my_filter_function( $arg ) {
    
    // Объявляем глобальную переменную
    global $my_global;

    // Используем $my_global для работы с $arg
    $arg = $arg . $my_global;

    return $arg;

}

function my_unset_function( $var_name ) {

    // Доступ к глобальной переменной
    $GLOBALS[$var_name];

    // Удаляем глобальную переменную
    unset($GLOBALS[$var_name];

}

Я самоучка и работаю только над своими сайтами, поэтому воспринимайте этот набросок с долей скепсиса. У меня это работает, но если здесь есть какие-то ошибки, буду благодарен, если кто-то более опытный укажет на них.

22 июн. 2020 г. 17:48:30
Комментарии

Глобальные переменные не защищены: любой может установить их в любое значение (подумайте о коллизиях имен), и их очень сложно отлаживать. В целом, они считаются плохой практикой.

fuxia fuxia
22 июн. 2020 г. 19:21:30
0

В моем ООП-решении я просто использовал переменную-член класса, которая вызывается в callback-функции. В этом примере post_title фильтруется по поисковому запросу:

class MyClass
{
  protected $searchterm = '';

  protected function myFunction()
  {
    query = [
      'numberposts' => -1,
      'post_type' => 'my_custom_posttype',
      'post_status' => 'publish'
    ];

    $this->searchterm = 'xyz';
    add_filter('posts_where', [$this, 'searchtermPostsWhere']);
    $myPosts = get_posts($query);
    remove_filter('posts_where', [$this, 'searchtermPostsWhere']);
  }

  public function searchtermPostsWhere($where)
  {
    $where .= ' AND ' . $GLOBALS['wpdb']->posts . '.post_title LIKE \'%' . esc_sql(like_escape($this->searchterm)) . '%\'';
    return $where;
  }
}
25 июл. 2020 г. 21:49:39
2
-1

Если вы создаете собственный хук, вот пример:

// допустим, у нас есть три параметра [ https://codex.wordpress.org/Function_Reference/add_filter ]
add_filter( 'filter_name', 'my_func', 10, 3 );
my_func( $first, $second, $third ) {
  // код
}

затем реализуем хук:

// [ https://codex.wordpress.org/Function_Reference/apply_filters ]
echo apply_filters( 'filter_name', $first, $second, $third );
14 мая 2015 г. 12:20:16
Комментарии

Это не передает информацию из регистрации в callback. Здесь просто указано, сколько параметров может принимать callback.

fuxia fuxia
18 мая 2015 г. 01:15:01

@fuxia, можешь предложить простое изменение, чтобы информация передавалась? Нужно ли просто добавить значения параметров после 3?

SherylHohman SherylHohman
25 мая 2019 г. 11:32:51
2
-1

Я знаю, что время прошло, но у меня были проблемы с передачей собственного параметра, пока я не обнаружил, что 4-й параметр в add_filter — это количество передаваемых параметров, включая контент для изменения. Поэтому, если вы передаёте 1 дополнительный параметр, число должно быть 2, а не 1, как в вашем случае.

add_filter('the_content', 'my_content', 10, 2, $my_param)

и использование

function my_content($content, $my_param) {...}
16 февр. 2017 г. 14:48:58
Комментарии

Вы уверены, что можно передать пятый параметр в add_filter? Согласно официальной документации, это некорректно. Вы тестировали свой ответ? Пожалуйста, будьте осторожны с распространением дезинформации.

plong0 plong0
23 сент. 2020 г. 22:02:20

Это работает не так, и мои тесты пока подтверждают, что это не так.

Jake Jake
9 дек. 2020 г. 18:52:47
0
-2

Я хотел сделать то же самое, но так как это невозможно, думаю, простым решением будет вызов другой функции, например add_filter('the_content', 'my_content_filter', 10, 1);

Затем функция my_content_filter() может просто вызвать my_content(), передавая любой нужный аргумент.

12 дек. 2017 г. 23:24:31