Передача параметра в функции фильтра и действия

52

Это способ передать мои собственные параметры функции в add_filterили add_action. Например, взгляните на следующий код:

function my_content($content, $my_param)
{
do something...
using $my_param here ...
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)
Аакаш Чакраварти
источник

Ответы:

78

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

Пример:

/**
 * Stores a value and calls any existing function with this value.
 */
class WPSE_Filter_Storage
{
    /**
     * Filled by __construct(). Used by __call().
     *
     * @type mixed Any type you need.
     */
    private $values;

    /**
     * Stores the values for later use.
     *
     * @param  mixed $values
     */
    public function __construct( $values )
    {
        $this->values = $values;
    }

    /**
     * Catches all function calls except __construct().
     *
     * Be aware: Even if the function is called with just one string as an
     * argument it will be sent as an array.
     *
     * @param  string $callback Function name
     * @param  array  $arguments
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function __call( $callback, $arguments )
    {
        if ( is_callable( $callback ) )
            return call_user_func( $callback, $arguments, $this->values );

        // Wrong function called.
        throw new InvalidArgumentException(
            sprintf( 'File: %1$s<br>Line %2$d<br>Not callable: %3$s',
                __FILE__, __LINE__, print_r( $callback, TRUE )
            )
        );
    }
}

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

Давайте создадим демонстрационную функцию ...

/**
 * Filter function.
 * @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>This works!</p>';
$param2 = 'This works too!';

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
);

/**
 * Add a string to post content
 *
 * @param  string $content
 * @param  string $string This is $param2 in our example.
 * @return string
 */
function t5_param_test( $content, $string )
{
    return "$content <p><b>$string</b></p>";
}

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

Фуксия
источник
17
Мало того, что вы получили положительный голос за качественный ответ на проблему, которая должна иметь встроенное решение в ядре WP , вы также получите ответ за пять месяцев спустя, чтобы обновить ваш ответ с примером закрытия PHP 5.3+.
Адам
1
Отличный ответ! Но как я могу позже удалить этот фильтр, созданный этой анонимной функцией?
Виниций Таварес
2
@ViniciusTavares Вы не можете. Подумайте, прежде чем использовать его. :)
fuxia
5
Обратите внимание, что если вы сохраняете анонимную функцию в переменной (например, $func = function() use ( $param1 ) { $param1; };и add_action( $func, 11);), вы можете удалить ее с помощьюremove_action( $func, 11 );
bonger
1
Но не рекомендуется использовать анонимные функции в плагинах или темах, которые вы публикуете для всего мира (вы можете использовать их в своих собственных проектах). Проблема в том, что вы не сможете отцепить их. Какой бы подход вы ни решили использовать, он должен быть отключен позже.
Mueyiwa Моисей Икоми
1

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

Показано здесь для уведомления администратора в WordPress Backend.

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('Message');
add_action('admin_notices', $func);
hornament
источник
1

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

$my_param = 'my theme name';
add_filter('the_content', function ($content) use ($my_param) {
    //$my_param is available for you now
    if (is_page()) {
        $content = $my_param . ':<br>' . $content;
    }
    return $content;
}, 10, 1);
wesamly
источник
1

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

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

и используя

function my_content($content, $my_param) {...}
giacoder
источник
1

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

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

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

// ... inside some class

private function myMethod() {
    $my_param = 'my theme name';
    add_filter('the_content', function ($content) use ($my_param) {
        // This is the anonymous closure that allows to pass 
        // whatever number of parameters you want via 'use' keyword.
        // This is just oneliner.
        // $my_param is available for you now via 'use' keyword above
        return $this->doThings($content, $my_param);
    }, 10, 2);
}

private function doThings($content, $my_param) {
    // Call here some other method to do some more things
    // however complicated you want.
    $morethings = '';
    if ($content = 'some more things') {
        $morethings = (new MoreClass())->get();
    }
    return $my_param . ':<br>' . $content . $morethings;
}
боб-12345
источник
0

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

// lets say we have three parameters  [ https://codex.wordpress.org/Function_Reference/add_filter ]
add_filter( 'filter_name', 'my_func', 10, 3 );
my_func( $first, $second, $third ) {
  // code
}

затем реализовать крюк:

// [ https://codex.wordpress.org/Function_Reference/apply_filters ]
echo apply_filters( 'filter_name', $first, $second, $third );
T.Todua
источник
Это не передает информацию от регистрации к обратному вызову. Он просто говорит, сколько параметров может принять обратный вызов.
fuxia
@fuxia, можете ли вы предложить простое изменение, чтобы информация передавалась? Можно ли просто добавить значения параметров после 3?
Шерил Хохман
0

Вы всегда можете использовать глобальный, не так ли?

  global $my_param;
samjco
источник
Это не дает ответа на вопрос. Как только у вас будет достаточно репутации, вы сможете комментировать любой пост ; вместо этого предоставьте ответы, которые не требуют разъяснений от автора . - Из обзора
cjbj
@cjbj На самом деле это так. Вопрос в том, можно ли передать параметры в «функцию», которая находится в add_filter или add_action. Было неясно, хочет ли пользователь передать его в функцию add_filter или add_action, даже если это предположение. :)
samjco
0

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

Например:

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

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получает столько параметров, сколько я хочу, только потому, что я передала анонимную функцию в качестве обратного вызова.

Маркос Резенде
источник
-1

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

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

Pierre-Verthume Larivière
источник