WP Cron No Se Ejecuta Cuando El Tiempo Transcurre
El objetivo
Quiero usar wp_schedule_single_event()
para ejecutar un evento único que me envíe un correo electrónico 8 minutos después de que el usuario envíe un formulario.
El problema
El siguiente código está en mi functions.php
:
function nkapi_send_to_system( $args ) {
wp_mail( 'xxx', 'xxx', $args );
}
add_action( 'nkapi_send', 'nkapi_send_to_system' );
function schedule_event( $id ) {
wp_schedule_single_event( current_time( 'timestamp' ) + 480, 'nkapi_send', array( $id ) );
}
Y el siguiente código se usa para llamar a schedule-event
:
schedule_event( $_SESSION['insert_id'] ); // la variable $_SESSION contiene un INT
Después de esperar más de 8 minutos no había ningún correo electrónico en mi bandeja de entrada.
Lo que intenté
Con el plugin Core Control es posible ver qué trabajos cron están programados.
Después de un par de cambios logré que funcionaran bastante bien y, mejor aún, cuando presiono "Ejecutar ahora", realmente recibo un correo electrónico en mi bandeja de entrada.
Pero ¿por qué los cron no se ejecutan cuando visito mi sitio después de 8 minutos? ¿Qué podría estar mal con este código? Debo decir que esta es mi primera vez usando WP Cron.
Intenté más
Después del comentario de vancoder decidí probar si el código funciona si pongo el siguiente código directamente en el functions.php
:
function schedule_event( $id ) {
wp_schedule_single_event( time(), 'nkapi_send', array( $id ) );
}
if ( isset( $_SESSION['insert_id'] ) ) {
if ( ! array_key_exists( 'insert_scheduled', $_SESSION ) || $_SESSION['insert_scheduled'] != $_SESSION['insert_id'] ) {
schedule_event( $_SESSION['insert_id'] );
$_SESSION['insert_scheduled'] = $_SESSION['insert_id'];
}
}
La desventaja de este código es que el usuario tiene que ir a otra página antes de que este código se ejecute. Pero por otro lado, esto tampoco funciona, así que ese no sería mi primer problema...
Primero, define tus programaciones personalizadas de cron jobs.
add_filter('cron_schedules', array($this, 'cron_schedules'));
public function cron_schedules($schedules){
$prefix = 'cron_';// Evitar conflicto con otros cron jobs. Ejemplo de referencia: cron_30_mins
$schedule_options = array(
'30_mins' => array(
'display' => '30 Minutos',
'interval' => '1800'
),
'1_hours' => array(
'display' => 'Hora',
'interval' => '3600'
),
'2_hours' => array(
'display' => '2 Horas',
'interval' => '7200'
)
);
/* Añade cada programación personalizada al sistema de cron jobs. */
foreach($schedule_options as $schedule_key => $schedule){
$schedules[$prefix.$schedule_key] = array(
'interval' => $schedule['interval'],
'display' => __('Cada '.$schedule['display'])
);
}
return $schedules;
}
Necesitas decidir dónde y cuándo programar realmente el evento.
Aquí tienes un ejemplo de fragmento de código que llama a un método personalizado de una clase:
$schedule = $this->schedule_task(array(
'timestamp' => current_time('timestamp'), // Determina cuándo programar la tarea.
'recurrence' => 'cron_30_mins',// Elige una de las programaciones establecidas anteriormente.
'hook' => 'custom_imap_import'// Establece el nombre de tu tarea cron.
));
Aquí está el código que realmente programa el evento:
private function schedule_task($task){
/* Debe tener información de la tarea. */
if(!$task){
return false;
}
/* Establece la lista de claves requeridas para la tarea. */
$required_keys = array(
'timestamp',
'recurrence',
'hook'
);
/* Verifica que exista la información necesaria de la tarea. */
$missing_keys = array();
foreach($required_keys as $key){
if(!array_key_exists($key, $task)){
$missing_keys[] = $key;
}
}
/* Comprueba si faltan claves. */
if(!empty($missing_keys)){
return false;
}
/* La tarea no debe estar ya programada. */
if(wp_next_scheduled($task['hook'])){
wp_clear_scheduled_hook($task['hook']);
}
/* Programa la tarea para que se ejecute. */
wp_schedule_event($task['timestamp'], $task['recurrence'], $task['hook']);
return true;
}
Ahora, todo lo que necesitas hacer es llamar al nombre de tu tarea cron personalizada. En este ejemplo, el nombre de la tarea cron es custom_imap_import
.
add_action('custom_imap_import', array($this, 'do_imap_import'));
public function do_imap_import(){
// .... Haz algo cuando se active el cron ....
}
Así que en este ejemplo, $this->do_imap_import();
se llama cada 30 minutos (asumiendo que tienes suficiente tráfico en tu sitio web).
Notas
Requiere una visita a la página para que tu cron se active en los momentos correctos.
Ejemplo: Si programaste una tarea en intervalos de 30 minutos, pero nadie visita tu sitio durante 4 horas, tu cron job no se activará hasta que un visitante llegue a tu sitio 4 horas después. Si realmente necesitas que tu tarea se active cada 30 minutos, se recomienda configurar un cron job legítimo a través de tu proveedor de alojamiento web para visitar tu sitio en los intervalos deseados.
¡Los cron jobs de WordPress no hacen que tu sitio web sea lento!
Quizás estés pensando qué pasa si el script cron tarda mucho tiempo en ejecutarse, ¿los visitantes tendrán que esperar hasta que el script se ejecute? ¡No! ¿Cómo es eso posible? Si miras el archivo wp-cron.php
encontrarás una línea
ignore_user_abort(true);
Es una configuración de php.ini
que establece que si dejas de cargar el sitio/script, el script no dejará de ejecutarse.
Si miras el archivo wp-includes/cron.php
encontrarás una línea como esta:
wp_remote_post( $cron_url,
array('timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters('https_local_ssl_verify', true)) );
Eso significa que WordPress esperará solo 0.01 segundos para activar la ejecución y luego abortará, pero como has establecido ignore_user_abort
en true
, el script seguirá ejecutándose. Esta funcionalidad es una gran ventaja para ejecutar scripts grandes en los cron jobs de WordPress.
Funciones disponibles para ayuda:

Esta es una respuesta notablemente exhaustiva, que hasta donde puedo ver, no aborda la pregunta real: por qué fallan todas las tareas programadas (incluidas las tareas principales).

Esta respuesta tiene como objetivo guiar al usuario en la dirección correcta para comprender y programar correctamente las tareas cron de WordPress.

Esto definitivamente me ayudó mucho a entender las programaciones cron con WordPress

¿En qué momento deberías hacer "add_action('custom_imap_import', array($this, 'do_imap_import'))", asumiendo una clase de plugin? ¿En el constructor?

@codecowboy Sí, o en algún lugar donde pueda cargarse cuando el cron esté listo para ejecutarse.

WordPress Core ya proporciona un intervalo hourly
, por lo que no necesitas usar 1_hours
.

Primero, ¿puedes confirmar que no tienes ningún plugin de caché activado? Los plugins de caché pueden interferir con los trabajos cron porque tus visitantes no reciben una página en vivo sino una versión en caché de tu página.
Si tienes un plugin de caché activado, puedes elegir una de tus páginas, agregar una exclusión en la configuración de tu plugin de caché para esa página específica, de modo que nunca se almacene en caché.
Luego tendrás que crear manualmente un trabajo cron (usando cPanel si estás en un entorno de hosting compartido o desde la terminal si es un servidor VPS/dedicado) que visitará esa página cada pocos minutos.
¡Espero que esto ayude!

El Cron de WordPress te permite programar tareas, pero solo se ejecutarán si hay una solicitud hecha al sitio. Por cada solicitud que WordPress recibe, verificará si hay trabajos cron para procesar y, de ser así, enviará una solicitud asíncrona a /wp-cron.php?doing_wp_cron
para procesar el trabajo. Si el tiempo programado de inicio de un trabajo pasa sin que haya una solicitud, entonces el proceso cron no se iniciará.
Dado que puedes ver y ejecutar tus trabajos programados, es posible que no haya solicitudes que activen el inicio del trabajo cron, especialmente si estás usando un plugin de caché. La mejor opción para delegar esto a un horario más regular es desactivar la verificación predeterminada en WordPress y usar crontab
.
Primero, para desactivar la verificación predeterminada (lo que puede ayudar un poco con el rendimiento del lado del cliente), agrega lo siguiente a wp-config.php
:
// Desactivar la verificación predeterminada de trabajos cron de WordPress en las cargas de página
define( 'DISABLE_WP_CRON', true );
Luego, crea una tarea para obtener la página wp-cron.php
una vez por minuto y procesar cualquier trabajo en el backend. Desde la línea de comandos, ingresa crontab -e
y luego agrega una línea similar a la siguiente:
*/1 * * * * /usr/bin/curl --silent http://ejemplo.com/wp-cron.php?doing_wp_cron=$(date +\%s.\%N) >/dev/null

Para cualquiera que esté protegiendo su sitio (de desarrollo) del acceso público, la Autenticación HTTP puede ser la causa de que WP Cron no funcione.
Por si puede ayudar a alguien, aquí está mi lista de cosas que hice antes de identificar y comprender los requisitos de WP Cron:
- Noté que los eventos se programaban correctamente y podían ejecutarse usando WP-CLI.
- Y también noté que acceder a /wp-cron.php?doing_wp_cron a través del navegador sí activaba las ejecuciones, como se sugiere en ej. 13625.
- La documentación oficial fue leída.
- Me aseguré de que DISABLE_WP_CRON no estuviera configurado.
- Y aprendí que ALTERNATE_WP_CRON era una solución temporal efectiva, aunque insatisfactoria.
- Para descartar errores de regresión, probé instalando un par de versiones antiguas de WordPress.
- Todos los plugins fueron desactivados y el tema cambiado al predeterminado, para aislar el problema.
- La gente mencionó el término loopbacks, pero al no tener errores o advertencias relacionadas lo consideré irrelevante.
- Finalmente, encontré una pregunta con una respuesta que realmente explica cómo depurar cron.
- Una vez que adjunté Xdebug a wp_cron(), pude ver que la ejecución terminaba en wp_remote_post(), lo cual claramente fallaba.
Cuando supe qué buscar, encontré este excelente artículo sobre las causas de los problemas con WP Cron de Jeff Starr. Al final del mismo, enlaza a su plugin wp-cron-http-auth.

Verifica que DISABLE_WP_CRON no esté configurado en tu archivo de configuración.
Si eso no funciona, intenta desactivar todos los plugins (excepto core control, aunque yo usaría wp-crontrol) y comprueba si tus trabajos principales funcionan. Si es así, estás experimentando interferencia de algún plugin.
De manera similar, prueba cambiar a un tema estándar de la serie twentysomething.
Si nada de esto hace diferencia, lo más probable es que sea un problema del alojamiento.

Verifica cualquier plugin que oculte WordPress.
¿Cómo comprobar si este es el problema?
- Navega a http(s)://tusitio.com/wp-cron.php Deberías ver una página completamente vacía.
- Además, debes ver en un gestor de tareas programadas una hora en "Próxima ejecución":
(no solo el texto "En cola" - sino una hora específica - para algunas entradas "En cola" está bien en ciertos casos, pero si es lo único que ves -> tu cron no funciona).
+1. No confíes en plugins que "verifican si cron está funcionando" - por ejemplo, el plugin WP Cron status checker mostró que cron funcionaba. Pero en realidad no era así.
Conclusión: Si obtienes un error 404, entonces desactiva a) no solo plugins de caché como otros sugieren b) sino también cualquier plugin que oculte WordPress.
