WP Cron Non Si Esegue Quando Il Tempo Scade
L'obiettivo
Voglio utilizzare wp_schedule_single_event()
per eseguire un evento singolo che mi invii un'email 8 minuti dopo che l'utente invia un modulo.
Il problema
Il seguente codice è nel mio 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 ) );
}
E il seguente codice viene utilizzato per chiamare schedule-event
:
schedule_event( $_SESSION['insert_id'] ); // la variabile $_SESSION contiene un INT
Dopo aver atteso più di 8 minuti non c'era nessuna email nella mia casella di posta.
Cosa ho provato
Con il plugin Core Control è possibile vedere quali cron job sono programmati.
Dopo un paio di modifiche sono riuscito a configurarli abbastanza correttamente e, ancora meglio, quando clicco su "Run Now", ricevo effettivamente un'email nella mia casella di posta.
Ma perché i cron non si eseguono quando visito il mio sito dopo 8 minuti? Cosa potrebbe esserci di sbagliato in questo codice? Devo dire che è la mia prima volta con WP Cron.
Ho provato altro
Dopo il commento di vancoder ho deciso di testare se il codice funziona inserendo direttamente il seguente codice nel 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'];
}
}
Lo svantaggio di questo codice è che l'utente deve andare su un'altra pagina prima che questo codice venga eseguito. Ma d'altra parte, questo non funziona neanche, quindi non sarebbe il mio primo problema...
Innanzitutto, definisci i tuoi programmi personalizzati per i cron job.
add_filter('cron_schedules', array($this, 'cron_schedules'));
public function cron_schedules($schedules){
$prefix = 'cron_';// Evita conflitti con altri cron. Esempio di riferimento: cron_30_mins
$schedule_options = array(
'30_mins' => array(
'display' => '30 Minuti',
'interval' => '1800'
),
'1_hours' => array(
'display' => 'Ora',
'interval' => '3600'
),
'2_hours' => array(
'display' => '2 Ore',
'interval' => '7200'
)
);
/* Aggiungi ogni programma personalizzato nel sistema dei cron job. */
foreach($schedule_options as $schedule_key => $schedule){
$schedules[$prefix.$schedule_key] = array(
'interval' => $schedule['interval'],
'display' => __('Ogni '.$schedule['display'])
);
}
return $schedules;
}
Devi decidere dove e quando programmare effettivamente l'evento.
Ecco un esempio di snippet di codice, che effettua una chiamata a un metodo personalizzato di classe:
$schedule = $this->schedule_task(array(
'timestamp' => current_time('timestamp'), // Determina quando programmare il task.
'recurrence' => 'cron_30_mins',// Scegli uno dei programmi impostati in precedenza.
'hook' => 'custom_imap_import'// Imposta il nome del tuo cron task.
));
Ecco il codice che effettivamente programma l'evento:
private function schedule_task($task){
/* Deve avere le informazioni del task. */
if(!$task){
return false;
}
/* Imposta l'elenco delle chiavi necessarie per il task. */
$required_keys = array(
'timestamp',
'recurrence',
'hook'
);
/* Verifica che esistano le informazioni necessarie per il task. */
$missing_keys = array();
foreach($required_keys as $key){
if(!array_key_exists($key, $task)){
$missing_keys[] = $key;
}
}
/* Controlla le chiavi mancanti. */
if(!empty($missing_keys)){
return false;
}
/* Il task non deve essere già programmato. */
if(wp_next_scheduled($task['hook'])){
wp_clear_scheduled_hook($task['hook']);
}
/* Programma l'esecuzione del task. */
wp_schedule_event($task['timestamp'], $task['recurrence'], $task['hook']);
return true;
}
Ora, tutto ciò che devi fare è effettuare una chiamata al nome del tuo cron task personalizzato. In questo esempio il nome del cron task è custom_imap_import
.
add_action('custom_imap_import', array($this, 'do_imap_import'));
public function do_imap_import(){
// .... Esegui operazioni quando il cron viene attivato ....
}
Quindi, in questo esempio, $this->do_imap_import();
viene chiamato ogni 30 minuti (supponendo che ci sia abbastanza traffico sul tuo sito web).
Note
Richiede una visita alla pagina affinché il tuo cron venga eseguito agli orari corretti.
Esempio: Se hai programmato un task a intervalli di 30 minuti, ma nessuno visita il tuo sito per 4 ore, il tuo cron job non verrà eseguito fino a quando un visitatore non arriverà sul tuo sito dopo 4 ore. Se hai davvero bisogno che il tuo task venga eseguito ogni 30 minuti, è consigliabile configurare un vero cron job tramite il tuo provider di hosting per visitare il tuo sito agli intervalli desiderati.
I cron job di WordPress non rallentano il tuo sito web!
Forse ti stai chiedendo: e se lo script del cron impiega molto tempo per essere eseguito, i visitatori dovranno aspettare fino al completamento dello script? No! Come è possibile? Se guardi il file wp-cron.php
troverai una riga
ignore_user_abort(true);
È una configurazione di php.ini
che imposta che se interrompi il caricamento del sito/script, lo script non si fermerà.
Se guardi il file wp-includes/cron.php
troverai una riga come questa:
wp_remote_post( $cron_url,
array('timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters('https_local_ssl_verify', true)) );
Ciò significa che WordPress attenderà solo 0.01 secondi per attivare l'esecuzione, poi interromperà la connessione ma, poiché hai impostato ignore_user_abort
a true
, lo script continuerà a essere eseguito. Questa funzionalità è un enorme vantaggio per eseguire script complessi nei cron job di WordPress.
Funzioni disponibili per aiutarti:

Questa è una risposta straordinariamente completa che, per quanto posso vedere, non affronta la vera domanda - ovvero perché tutti i task programmati (compresi quelli core) falliscono.

Questa risposta ha lo scopo di guidare l'utente nella giusta direzione per comprendere e programmare correttamente i task cron di WordPress.

Questo mi ha sicuramente aiutato molto a capire le pianificazioni cron con WordPress

A che punto dovresti fare "add_action('custom_imap_import', array($this, 'do_imap_import'))", assumendo una classe di plugin? Nel costruttore?

@codecowboy Sì, o da qualche parte possa essere caricato quando il cron è pronto per essere eseguito.

Il core fornisce già un intervallo hourly
, quindi non è necessario utilizzare 1_hours
.

Per prima cosa, puoi confermare di non avere alcun plugin di caching attivato? I plugin di caching possono interferire con i cron job perché i tuoi visitatori non ricevono una pagina live ma una versione memorizzata nella cache della tua pagina.
Se hai un plugin di caching attivo, puoi scegliere una delle tue pagine, aggiungere un'esclusione nelle impostazioni del tuo plugin di caching per quella pagina in modo che non venga mai memorizzata nella cache.
Dopodiché dovrai creare manualmente un cron job (usando cpanel se sei su un ambiente di hosting condiviso o dal terminale se si tratta di un server VPS/dedicato) che visiterà quella pagina ogni pochi minuti.
Spero che questo ti aiuti!

WordPress Cron ti consente di pianificare attività, ma queste verranno eseguite solo se viene effettuata una richiesta al sito. Per ogni richiesta che WordPress riceve, controllerà se ci sono lavori cron da elaborare e, in caso affermativo, invierà una richiesta asincrona a /wp-cron.php?doing_wp_cron
per elaborare il lavoro. Se l'orario programmato di inizio di un lavoro passa senza una richiesta, il processo cron non verrà avviato.
Poiché sei in grado di visualizzare ed eseguire i tuoi lavori pianificati, è possibile che non ci siano richieste che attivino l'avvio del lavoro cron, specialmente se stai utilizzando un plugin di caching. L'opzione migliore per delegare questo a una pianificazione più regolare è disabilitare il controllo predefinito in WordPress e utilizzare crontab
.
Innanzitutto, per disabilitare il controllo predefinito (che può aiutare un po' con le prestazioni lato client), aggiungi quanto segue a wp-config.php
:
// Disabilita il controllo predefinito per i lavori cron di WordPress durante il caricamento delle pagine
define( 'DISABLE_WP_CRON', true );
Successivamente, crei un'attività per recuperare la pagina wp-cron.php
una volta al minuto per elaborare eventuali lavori sul backend, dalla riga di comando inserisci crontab -e
e poi aggiungi una riga simile alla seguente:
*/1 * * * * /usr/bin/curl --silent http://example.com/wp-cron.php?doing_wp_cron=$(date +\%s.\%N) >/dev/null

Per chi protegge il proprio sito (di sviluppo) dall'accesso pubblico, l'autenticazione HTTP può essere la causa del mancato funzionamento di WP Cron.
Nel caso possa essere utile a qualcuno, ecco la lista delle cose che ho fatto prima di identificare e comprendere i requisiti di WP Cron:
- Ho notato che gli eventi erano correttamente schedulati e potevano essere eseguiti utilizzando WP-CLI.
- E ho anche notato che accedendo a /wp-cron.php?doing_wp_cron tramite browser venivano effettivamente attivate le esecuzioni, come suggerito ad esempio in 13625.
- La documentazione ufficiale è stata letta.
- Mi sono assicurato che DISABLE_WP_CRON non fosse impostato.
- E ho appreso che ALTERNATE_WP_CRON era una soluzione alternativa efficace, seppur insoddisfacente.
- Per escludere bug di regressione, ho provato a installare un paio di versioni precedenti di WordPress.
- Tutti i plugin sono stati disabilitati e il tema è stato cambiato con quello predefinito, per isolare il problema.
- Alcuni menzionavano il termine loopback, ma poiché non ricevevo errori o avvisi correlati, l'ho considerato irrilevante.
- Infine, ho trovato una domanda con una risposta che spiegava effettivamente come eseguire il debug di cron.
- Dopo aver collegato Xdebug a wp_cron(), ho potuto vedere che l'esecuzione finiva in wp_remote_post(), che chiaramente falliva.
Sapendo cosa cercare, ho trovato questo ottimo articolo sulle cause dei problemi di WP Cron di Jeff Starr. Alla fine dell'articolo, egli rimanda al suo plugin wp-cron-http-auth.

Verifica che DISABLE_WP_CRON non sia impostato nel tuo file di configurazione.
Se non è quello il problema, prova a disabilitare tutti i plugin (tranne core control - anche se consiglio wp-crontrol) e verifica se i lavori di base funzionano. Se funzionano, significa che c'è un'interferenza da parte di qualche plugin.
Allo stesso modo, prova a passare a un tema standard come twentysomething.
Se nessuna di queste soluzioni fa differenza, è probabile che si tratti di un problema di hosting.

Controlla qualsiasi plugin che nasconda WordPress.
Come verificare se questo è il problema?
- Naviga su http(s)://yoursite.com/wp-cron.php Dovresti vedere una pagina vuota. Completamente vuota.
- Inoltre, nel gestore dei cron job dovresti vedere un orario sotto "Prossima esecuzione":
(non solo il testo "In coda" - ma un orario specifico - per alcune voci "In coda" è accettabile in certi casi, ma se è l'unica cosa che vedi -> il tuo cron non funziona.)
+1. Non fidarti di alcun plugin che "verifica se il cron funziona" - ad esempio, il plugin WP Cron status checker ha mostrato che il cron funzionava. Ma in realtà non era così.
Conclusione: Se ricevi un errore 404 - allora disattiva a) non solo i plugin di caching come suggerito da altri b) ma anche qualsiasi plugin che nasconda WordPress.
