Pasando un parámetro a funciones de filtro y acción
Existe una forma de pasar mis propios parámetros a la función en add_filter
o add_action
.
Por ejemplo, observa el siguiente código:
function my_content($content, $my_param)
{
// hacer algo...
// usando $my_param aquí ...
return $content;
}
add_filter('the_content', 'my_content', 10, 1);
¿Puedo pasar mi propio parámetro? ¿algo como:
add_filter('the_content', 'my_content($my_param)', 10, 1)
o
add_filter('the_content', 'my_content', 10, 1, $my_param)
Por defecto esto no es posible. Hay soluciones alternativas si lo haces de manera orientada a objetos.
Podrías crear una clase para almacenar los valores que quieras usar más tarde.
Ejemplo:
/**
* Almacena un valor y llama a cualquier función existente con este valor.
*/
class WPSE_Filter_Storage
{
/**
* Rellenado por __construct(). Usado por __call().
*
* @type mixed Cualquier tipo que necesites.
*/
private $values;
/**
* Almacena los valores para su uso posterior.
*
* @param mixed $values
*/
public function __construct( $values )
{
$this->values = $values;
}
/**
* Captura todas las llamadas a funciones excepto __construct().
*
* Ten en cuenta: Incluso si la función se llama con solo una cadena como
* argumento, se enviará como un array.
*
* @param string $callback Nombre de la función
* @param array $arguments
* @return mixed
* @throws InvalidArgumentException
*/
public function __call( $callback, $arguments )
{
if ( is_callable( $callback ) )
return call_user_func( $callback, $arguments, $this->values );
// Función incorrecta llamada.
throw new InvalidArgumentException(
sprintf( 'Archivo: %1$s<br>Línea %2$d<br>No se puede llamar: %3$s',
__FILE__, __LINE__, print_r( $callback, TRUE )
)
);
}
}
Ahora puedes llamar a la clase con cualquier función que desees – si la función existe en algún lugar, se llamará con tus parámetros almacenados.
Vamos a crear una función de demostración ...
/**
* Función de filtro.
* @param array $content
* @param array $numbers
* @return string
*/
function wpse_45901_add_numbers( $args, $numbers )
{
$content = $args[0];
return $content . '<p>' . implode( ', ', $numbers ) . '</p>';
}
... y usarla una vez ...
add_filter(
'the_content',
array (
new WPSE_Filter_Storage( array ( 1, 3, 5 ) ),
'wpse_45901_add_numbers'
)
);
... y otra vez ...
add_filter(
'the_content',
array (
new WPSE_Filter_Storage( array ( 2, 4, 6 ) ),
'wpse_45901_add_numbers'
)
);
Salida:
La clave es la reutilización: Puedes reutilizar la clase (y en nuestros ejemplos también la función).
PHP 5.3+
Si puedes usar una versión de PHP 5.3 o superior, los cierres (closures) lo harán mucho más fácil:
$param1 = '<p>¡Esto funciona!</p>';
$param2 = '¡Esto también funciona!';
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
);
/**
* Añade una cadena al contenido del post
*
* @param string $content
* @param string $string Esto es $param2 en nuestro ejemplo.
* @return string
*/
function t5_param_test( $content, $string )
{
return "$content <p><b>$string</b></p>";
}
El inconveniente es que no puedes escribir pruebas unitarias para los cierres.

No solo obtienes un voto a favor por una respuesta de calidad a un problema que debería tener una solución incorporada en el núcleo de WP, sino que también obtienes uno por regresar cinco meses después para actualizar tu respuesta con el ejemplo de cierre (closure) para PHP 5.3+.

¡Excelente respuesta! Pero, ¿cómo puedo hacer para eliminar este filtro creado por esta función anónima más adelante?

Sin embargo, ten en cuenta que si guardas la función anónima en una variable (por ejemplo, $func = function() use ( $param1 ) { $param1; };
y add_action( $func, 11);
) entonces puedes eliminarla mediante remove_action( $func, 11 );

Pero no es recomendable usar funciones anónimas en plugins o temas que vayas a distribuir públicamente (puedes usarlas en tus propios proyectos). El problema con esto es que no podrás desengancharlas. Cualquier enfoque que decidas usar debería permitir ser desenganchado posteriormente.

Usa funciones anónimas en PHP (funciones anónimas):
$my_param = 'mi nombre de tema';
add_filter('the_content', function ($content) use ($my_param) {
//$my_param está disponible ahora
if (is_page()) {
$content = $my_param . ':<br>' . $content;
}
return $content;
}, 10, 1);

La forma correcta, realmente corta y más eficiente de pasar cualquier número de argumentos a los filtros y acciones de WordPress es de @Wesam Alalem aquí, que utiliza un cierre (closure).
Solo agregaría que podrías hacerlo aún más claro y mucho más flexible separando el método que realiza la acción del cierre anónimo. Para esto, simplemente llamas al método desde el cierre de la siguiente manera (ejemplo modificado de la respuesta de @Wesam Alalem).
De esta forma, puedes escribir una lógica tan larga o complicada como desees léxicamente fuera del cierre que utilizas para llamar al método que realiza la acción.
// ... dentro de alguna clase
private function myMethod() {
$my_param = 'mi nombre de tema';
add_filter('the_content', function ($content) use ($my_param) {
// Este es el cierre anónimo que permite pasar
// cualquier número de parámetros que desees mediante la palabra clave 'use'.
// Esto es solo una línea.
// $my_param está disponible para ti ahora a través de la palabra clave 'use' anterior
return $this->doThings($content, $my_param);
}, 10, 2);
}
private function doThings($content, $my_param) {
// Llama aquí a algún otro método para hacer más cosas
// por muy complicadas que sean.
$morethings = '';
if ($content = 'algunas cosas más') {
$morethings = (new MoreClass())->get();
}
return $my_param . ':<br>' . $content . $morethings;
}

Crea una función con los argumentos necesarios que retorne otra función. Pasa esta función (función anónima, también conocida como closure) al hook de WordPress.
Mostrado aquí para un aviso de administración en el backend de 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('Mensaje');
add_action('admin_notices', $func);

Como se mencionó en otras respuestas, pasar un parámetro a la función de callback no es posible por defecto. La POO y las funciones anónimas de PHP son soluciones alternativas PERO:
- Tu código podría no estar orientado a objetos
- Podrías necesitar eliminar ese filtro posteriormente
Si ese es tu caso, existe otra solución alternativa que puedes utilizar: haz uso de las funciones add_filter
y apply_filters
para hacer que ese parámetro que deseas pasar esté disponible en la función de callback:
// Solución alternativa para "guardar" el parámetro que se pasará a tu función de callback.
add_filter( 'pass_param', function() use ( $param ){ return $param; } );
// Enlaza tu función para filtrar lo que necesites filtrar.
add_filter( 'actual_filter', 'myCallback' );
// Tu función de callback que realmente filtra lo que necesites.
function myCallback()
{
// Obtiene el parámetro que no pudimos pasar directamente a esta función.
$param = apply_filters( 'pass_param', '' );
// Haz lo que necesites con el parámetro pasado alternativamente para usarlo en el filtrado.
return $param;
}

Esto no proporciona una respuesta a la pregunta. Una vez que tengas suficiente reputación podrás comentar cualquier publicación; en su lugar, proporciona respuestas que no requieran aclaración del que pregunta. - De la revisión

@cjbj En realidad sí lo hace. La pregunta es si se pueden pasar parámetros a la "función" que está en add_filter o add_action. No estaba claro si el usuario quería pasarlo en la propia función add_filter o add_action, aunque esa sea la suposición. :)

A pesar de llamar a una función directamente, hazlo de una manera más elegante: pasa una función anónima como callback.
Por ejemplo:
Tengo una única función para traducir el título, el contenido y el extracto de mis publicaciones. Por lo tanto, necesito pasar a esta función principal algunos argumentos que indiquen quién está llamando.
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' );
});
Así, la función principal translate_text
recibe tantos parámetros como desee, simplemente porque he pasado una función anónima como callback.

Estoy de acuerdo en que la respuesta de fuxia anterior proporciona los enfoques preferidos. Pero mientras intentaba entender la solución OOP, encontré una manera de hacerlo que establece y luego elimina tanto el filtro como una variable global:
function my_function() {
// Declarar la variable global y establecerla en algo
global $my_global;
$my_global = 'algo';
// Añadir el filtro
add_filter( 'some_filter', 'my_filter_function' );
// Hacer lo que sea que necesitabas el filtro para
echo $filtered_stuff;
// Eliminar el filtro (para que no afecte a algo más que se ejecute después)
remove_filter( 'some_filter', 'my_filter_function' );
// Eliminar la global (porque no nos gustan las variables globales flotando en nuestro código)
my_unset_function( 'my_global' );
}
function my_filter_function( $arg ) {
// Declarar la global
global $my_global;
// Usar $my_global para hacer algo con $arg
$arg = $arg . $my_global;
return $arg;
}
function my_unset_function( $var_name ) {
// Declarar la global
$GLOBALS[$var_name];
// Eliminar la global
unset($GLOBALS[$var_name];
}
Soy un desarrollador sin formación y trabajo estrictamente en mis propios sitios, así que por favor tome este boceto con cautela. Funciona para mí, pero si hay algo incorrecto en lo que estoy haciendo aquí, agradecería que alguien con más conocimiento lo señalara.

En mi solución de POO simplemente usé una variable miembro de la clase que es llamada en la función de callback. En este ejemplo el post_title es filtrado por un término de búsqueda:
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;
}
}

si creas tu propio hook, aquí tienes un ejemplo.
// digamos que tenemos tres parámetros [ https://codex.wordpress.org/Function_Reference/add_filter ]
add_filter( 'filter_name', 'my_func', 10, 3 );
my_func( $first, $second, $third ) {
// código
}
luego implementa el hook:
// [ https://codex.wordpress.org/Function_Reference/apply_filters ]
echo apply_filters( 'filter_name', $first, $second, $third );

Esto no pasa la información del registro al callback. Solo indica cuántos parámetros puede aceptar el callback.

Ya sé que ha pasado tiempo, pero tuve algunos problemas para pasar mi propio parámetro hasta que descubrí que el cuarto parámetro en add_filter es el número de parámetros pasados incluyendo el contenido a modificar. Así que si pasas 1 parámetro adicional, el número debería ser 2 y no 1 en tu caso
add_filter('the_content', 'my_content', 10, 2, $my_param)
y usando
function my_content($content, $my_param) {...}

¿Estás seguro de que puedes pasar un quinto parámetro a add_filter
? Según la documentación oficial, esto no es correcto. ¿Has probado tu respuesta? Por favor, ten cuidado con difundir información errónea.

Tenía la esperanza de hacer lo mismo pero como no es posible, supongo que una solución simple es llamar a una función diferente como
add_filter('the_content', 'my_content_filter', 10, 1);
Entonces my_content_filter() puede simplemente llamar a my_content() pasando cualquier argumento que desee.
