Cómo Pasar Variables Externas a Filtros/Acciones
Me encuentro necesitando pasar datos personalizados a un filtro proporcionado por un plugin de terceros. Todas las formas que he visto para hacer esto son realmente complicadas y difíciles de entender.
Toma este ejemplo:
$score = 42; //Algún cálculo loco que no quiero repetir.
function add_score_to_title($title) {
return 'Resultados del Cuestionario (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
¿Cómo puedo pasar la variable $score
a add_score_to_title()
?
Lo que terminé haciendo fue agregar mi variable al objeto global $wp
. Así que terminas con esto:
global $wp;
$score = 42; //Algún cálculo loco que no quiero repetir.
$wp->some_random_name_for_score = $score;
function add_score_to_title($title) {
global $wp;
$score = $wp->some_random_name_for_score;
return 'Resultados del Cuestionario (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
¿Sucio? Tal vez. ¿Simple? ¡Sí! ¿Hay desventajas en esta técnica? Por favor, discutamos.
ACTUALIZACIÓN Aquí está el código completo en cuestión -> http://pastebin.com/fkSXY04m
Tienes al menos dos opciones:
- Globalizar la variable deseada y luego hacer referencia a ella dentro del callback
- Encapsular la lógica de cálculo de puntuación en una función, luego hacer referencia a ella dentro del callback
Globalizar la Variable
<?php
global $score;
$score = 42; // Algún cálculo loco que no quiero repetir.
function add_score_to_title($title) {
global $score;
return 'Resultados del Quiz (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Encapsular el Cálculo de Puntuación
Si solo necesitas el cálculo de puntuación dentro del filtro, lleva la lógica al callback mismo:
<?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 'Resultados del Quiz (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Mejor aún, podrías encapsular tu cálculo de puntuación en una función propia, y luego llamar a esa función dentro de tu 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 'Resultados del Quiz (' . $score . '/') - ' . $title;
}
add_filter( 'aioseop_title_single', 'add_score_to_title');
?>
Si tienes problemas al hacer referencia al objeto $_POST
, también puedes registrar tu variable de consulta y luego usar get_query_var()
internamente para obtener los datos:
function add_score_query_vars( $query_vars ) {
$query_vars[] = 'Q';
return $query_vars;
}
add_filter( 'query_vars', 'add_score_query_vars' );
Con esto implementado, $_POST['Q']
puede reemplazarse con get_query_var('Q')
.

Esto no tiene nada que ver con el número de argumentos pasados a la función por apply_filters...

Probé el método #1 que mencionas de globalizar la variable. No funciona. El parámetro accepted_args tampoco me ayuda ya que no tengo control sobre qué variables se pasan a la función de callback.

Perdón, tenías razón. Calculé $score
primero y luego lo globalicé. No es de extrañar que no funcionara. ¡Gracias!

-1. La primera opción expone una variable en el estado global, la segunda opción no funciona...

Actualizado para reflejar que estaba equivocado sobre la segunda opción. Pero ¿qué tiene de malo exponer una variable global? Ciertamente es mejor que usar una función anónima en un callback. :)

No, no lo es. Y aquí hay algunas razones por las que deberías evitar esta práctica. Tal vez esté bien para su caso específico, pero ¿y si alguien realmente construye un plugin público usando tu solución?

"¿Las variables globales son malvadas?"? ¿En serio? Entonces mejor que todo el código de WordPress esté reescrito, ya que depende de bastantes variables globales.

Exactamente. Esta es una de las razones por las que el código base de WP es uno de los peores en la web. De todos modos, WP tiene una excusa - la compatibilidad requerida con plugins diseñados para versiones antiguas. Tú no :)

Sí... voy a dejar ese tema aparte. Gracias por señalar el problema con la segunda opción; la he eliminado de la pregunta. Mantengo que es aceptable usar variables globales. Si supiera cómo se genera o devuelve la salida del Plugin, probablemente podría ofrecer una mejor solución.

Consulta la respuesta actualizada; después de revisar el Pastebin, he ofrecido dos métodos que preferiría en lugar de usar una variable global.

Mejor, pero él quiere que el "score" sea accesible más adelante en el script, sin volver a calcularlo, por lo que el uso de una variable "static" tiene sentido aquí.

Usando una función para calcular el score: ¿no se podría almacenar el cálculo en la caché de objetos?

a finales de 2015 y WP todavía depende de variables globales malignas. La excusa ya está muy pasada. Y +1 por la sugerencia de globalizar. En mi caso, el callback necesitaba acceder al $user
que tuve que globalizar ya que mi llamada apply_filters
se iniciaba desde dentro de una función.

Estos Globales son malignos, la gente necesita relajarse. Me gusta bastante esta respuesta. El OP preguntó cómo se podían pasar variables entre funciones. Esta respuesta explica varias formas de hacerlo. Comienza con globales, que es mejor evitar, pero aún así son algo que existe y resuelve el problema en cuestión. Luego, la respuesta procede a explicar varias opciones mejores que podrían funcionar en algunas circunstancias.

function add_score_to_title($title = false) {
static $score = false;
if($score === false){
// hacer cálculo
}
// llamada del plugin (filtro)
if($title !== false)
return 'Resultados del Quiz (' . $score . ') - ' . $title;
// tu llamada
return $score;
}
Llama a la función en cualquier parte de tu script para obtener el puntaje, solo se calculará una vez.
Otra forma, usando funciones anónimas:
// hacer el cálculo
$score = 'xxx';
add_filter('aioseop_title_single', function($title) use($score){
return 'Resultados del Quiz (' . $score . ') - ' . $title;
});

Las funciones anónimas no deben usarse en llamadas a add_filter()
o add_action()
. No se pueden eliminar mediante remove_function()
.

Te refieres a remove_filter, que se usa principalmente para eliminar filtros integrados, no filtros agregados por plugins/temas...

El siguiente ejemplo muestra la variable $my_calculation
en el ámbito global, sin embargo, desde nuestra función local necesitamos declarar global $my_calculation
para poder acceder a la variable en el ámbito global.
<?php
$my_calculation = 'resultado!';
function my_function() {
global $my_calculation;
return $my_calculation;
}
add_filter( 'function_something_here', 'my_function');
?>
Esta es solo una forma de hacerlo y parece ser ordenada. ¿Funcionaría para ti?

Una declaración menos de "global". Mira su segundo ejemplo, ¡declara global $wp dos veces!

¿Puedes usar print
o echo
para mostrar tu resultado y asegurarte de que tu función está funcionando correctamente antes de pasarla al filtro?

¡Ups! Globalizar $score
sí funciona. Me equivoqué al asignar primero un valor a $score
y luego globalizarlo, lo cual claramente no funciona. Hacerlo correctamente, globalizando primero $score
y luego asignándole un valor, funciona como se esperaba. Gracias a todos.
