¿Cómo usar do_action y obtener un valor de retorno?
Tenemos el siguiente escenario.
Agrego una acción para limpiar logs de la base de datos:
add_action( 'myplugin_clean_logs', array( 'MyPlugin_Logs', 'clean_logs' ) );
Ahora quiero ejecutar esta acción periódicamente:
wp_schedule_event( current_time( 'timestamp' ), 'daily', 'myplugin_clean_logs' );
y ejecutarla manualmente:
do_action( 'myplugin_clean_logs' );
El método MyPlugin_Logs::clean_logs
devuelve el conteo de filas afectadas o false si algo salió mal.
Ahora quiero mostrar el número de filas que han sido eliminadas. Me imagino algo como esto:
$affected_rows = do_action( 'myplugin_clean_logs' );
echo $affected_rows . ' entradas han sido eliminadas.';
Pero como do_action
no devolverá ningún valor, no tengo idea de cómo obtener el valor de retorno.
¿Debería ejecutar el método directamente en una ejecución manual, pero usar la acción en eventos programados?

Lo interesante es que un filtro es lo mismo que una acción, solo que devuelve un valor, así que simplemente configúralo como un filtro:
add_filter( 'myplugin_clean_logs', array( 'MyPlugin_Logs', 'clean_logs' ) );
Entonces algo como:
$affected_rows = '';
$affected_rows = apply_filters( 'myplugin_clean_logs', $affected_rows );
debería pasar $affected_rows
a clean_logs()
(y cualquier otra función que hayas enganchado a myplugin_clean_logs
) y asignar el valor de retorno de nuevo a $affected_rows
.

votado negativo porque esto es hackear código en lugar de desarrollar software. Si las acciones fueran solo un subconjunto de filtros no habría necesidad de ellas. Cron no puede pasar valores, por lo tanto no debería engancharse como un filtro incluso si el código problemático del núcleo te permite hacer tales trucos :)

Entiendo el punto. Comprendo la intención de las dos cosas diferentes, pero al mirar el código del núcleo aquí, todo el asunto de do_action()
no es más que un hack elaborado de apply_filters()
:)

no es el único mal diseño en el núcleo, lo cual en parte es lo que lleva a la confusión que genera preguntas como esta

Tenemos que trabajar con lo que tenemos, así que aunque entiendo el punto de vista de Mark, sigo pensando que esta es una respuesta legítima - a menos que, por supuesto, el núcleo cambie este enfoque en el futuro, pero creo que eso es poco probable debido a los enormes problemas de compatibilidad hacia atrás que introduciría.

Gracias, @TimMalone. Realmente aprecio la objeción de @mark-kaplun. Mi respuesta describe cómo sortear que do_action()
no devuelva un valor en lugar de cómo diseñar una solución acorde con la intención de do_action()
. Si alguien es capaz de hacer lo que él pide, esa respuesta merecería ser la aceptada. Mi primer pensamiento sería que el método enganchado (asumiendo que el OP está usando un diseño OOP para este plugin) guarde su resultado en una propiedad protegida de la clase del plugin y luego escribir un rápido getter para extraerlo en algún punto posterior. ¡Pero eso es solo una idea al azar!

@Casper la documentación de WordPress distingue explícitamente entre acciones y filtros. Según lo que entendí, un filtro está ahí para filtrar cosas realmente y como tal es más una herramienta que un ejecutor. Tal vez esto es solo teoría y más una diferencia de diseño de código que un caso del mundo real. Al principio tuve una idea similar de pasar una variable por referencia (aunque no lo probé, si funciona), pero lo evité para no estropear el código.

@TimMalone, a veces cuando tienes un problema de codificación confuso, deberías detenerte y preguntarte si es un problema de codificación o un problema de diseño

@Aley, lo siento, en realidad escribí un comentario a la pregunta pero aparentemente olvidé enviarlo, pero puedes captar la idea de lo que pienso (necesitas manejarlo diferente en ambos casos) de los comentarios aquí

¿Por qué no simplemente usar una llamada a función PHP en su lugar? De esa manera no necesitas do_action o apply_filters. Coloca la función dentro del archivo functions.php.

Esta es una pregunta muy antigua pero para responder a la pregunta original "¿Cómo usar do_action y obtener un valor de retorno?" para cualquiera que esté buscando, puedes hacerlo usando el buffer de salida.
ob_start();
do_action( 'myplugin_clean_logs' );
$action_data = ob_get_clean();
De esta manera puedes almacenar el contenido de do_action en una variable.

Nunca he usado esta función y no la he probado, pero ¿podría funcionar? do_action_ref_array().
function myplugin_clean_logs_fn() {
$args = array(
'param1' => 'val1',
'param2' => 'val2',
'affected_rows' => 0,
);
do_action_ref_array( 'myplugin_clean_logs', &$args );
return $args['affected_rows'];
}
// LLAMAR LA FUNCIÓN
$affected_rows = my_plugin_clean_logs();
echo $affected_rows .' registr'. ($args['affected_rows']*1===1?'o':'os') .' eliminados.';
// PROGRAMARLA
add_action('myplugin_clean_logs_call_fn', 'myplugin_clean_logs_fn');
wp_schedule_event( current_time( 'timestamp' ), 'daily', 'myplugin_clean_logs_call_fn' );
// UN FILTRO DE EJEMPLO
add_action('myplugin_clean_logs', function($args) {
// Proceso de limpieza
// Por cada registro afectado, incrementar $args['affected_rows'] según corresponda
}, 10, 3);
Si eso no funciona, ¿por qué no simplemente filtrar como sugirió Caspar? Al fin y al cabo, ese es el propósito de un filtro, y en este caso el número de registros afectados es lo que se está filtrando. (Extraño el viejo MortCore. ¿Alguien recuerda cómo manejaba los valores de retorno, paso por referencia y argumentos con solo una función de tres parámetros?).

Esta es una respuesta horrible, ya que pasar y modificar valores por referencia es una muy mala práctica. Honestamente, esta respuesta realmente no aporta valor en el contexto de la pregunta y probablemente debería eliminarse o cambiarse a un comentario. Además, usar funciones anónimas con hooks también es mala práctica, ya que las hace imposibles de desenganchar.

Estoy de acuerdo por las mismas razones mencionadas anteriormente, que este no es un camino recomendado. Si por alguna razón necesitas obtener un valor de retorno de una acción, y necesitas algo rápido y sucio, preferiría la solución de Caspars. Si estás desarrollando algo con un ciclo de vida por delante, buscaría una forma más robusta. Ahora que lo pienso, ¿qué tal los admin notices? https://developer.wordpress.org/reference/hooks/admin_notices/
