WP Cron nu execută când timpul s-a scurs
Obiectivul
Vreau să folosesc wp_schedule_single_event( )
pentru a executa un eveniment singular care să-mi trimită un e-mail la 8 minute după ce utilizatorul trimite un formular.
Problema
Următorul cod este în fișierul meu 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 ) );
}
Și următorul cod este folosit pentru a apela schedule-event
:
schedule_event( $_SESSION['insert_id'] ); // variabila $_SESSION conține un INT
După ce am așteptat mai mult de 8 minute nu am primit niciun e-mail în inbox.
Ce am încercat
Cu plugin-ul Core Control este posibil să vezi ce sarcini cron sunt programate.
După câteva modificări am reușit să le configurez corect și, mai mult, când apăs "Run Now", primesc într-adevăr un e-mail în inbox.
Dar de ce nu se execută sarcinile cron când vizitez site-ul meu după 8 minute. Ce ar putea fi greșit în acest cod? Trebuie să menționez că este prima dată când folosesc WP Cron.
Am încercat mai mult
După comentariul lui vancoder am decis să testez dacă codul funcționează dacă pun următorul cod direct în 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'];
}
}
Dezavantajul acestui cod este că utilizatorul trebuie să meargă pe altă pagină înainte ca acest cod să fie executat. Dar pe de altă parte, nici acesta nu funcționează, așa că aceasta nu ar fi prima mea problemă...

În primul rând, definește programările personalizate pentru joburile cron.
add_filter('cron_schedules', array($this, 'cron_schedules'));
public function cron_schedules($schedules){
$prefix = 'cron_';// Evită conflictele cu alte joburi cron. Exemplu de referință: cron_30_mins
$schedule_options = array(
'30_mins' => array(
'display' => '30 de minute',
'interval' => '1800'
),
'1_hours' => array(
'display' => 'Oră',
'interval' => '3600'
),
'2_hours' => array(
'display' => '2 ore',
'interval' => '7200'
)
);
/* Adaugă fiecare programare personalizată în sistemul de joburi cron. */
foreach($schedule_options as $schedule_key => $schedule){
$schedules[$prefix.$schedule_key] = array(
'interval' => $schedule['interval'],
'display' => __('La fiecare '.$schedule['display'])
);
}
return $schedules;
}
Trebuie să decizi unde și când să programezi efectiv evenimentul.
Iată un exemplu de fragment de cod care face un apel către o metodă personalizată a unei clase:
$schedule = $this->schedule_task(array(
'timestamp' => current_time('timestamp'), // Determină când să programezi sarcina.
'recurrence' => 'cron_30_mins',// Alege una dintre programările definite anterior.
'hook' => 'custom_imap_import'// Setează numele jobului tău cron.
));
Iată codul care programează efectiv evenimentul:
private function schedule_task($task){
/* Trebuie să existe informații despre sarcină. */
if(!$task){
return false;
}
/* Setează lista de chei necesare pentru sarcină. */
$required_keys = array(
'timestamp',
'recurrence',
'hook'
);
/* Verifică existența informațiilor necesare pentru sarcină. */
$missing_keys = array();
foreach($required_keys as $key){
if(!array_key_exists($key, $task)){
$missing_keys[] = $key;
}
}
/* Verifică cheile lipsă. */
if(!empty($missing_keys)){
return false;
}
/* Sarcina nu trebuie să fie deja programată. */
if(wp_next_scheduled($task['hook'])){
wp_clear_scheduled_hook($task['hook']);
}
/* Programează sarcina să ruleze. */
wp_schedule_event($task['timestamp'], $task['recurrence'], $task['hook']);
return true;
}
Acum, tot ce trebuie să faci este să apelezi numele jobului tău cron personalizat. În acest exemplu, numele jobului cron este custom_imap_import
.
add_action('custom_imap_import', array($this, 'do_imap_import'));
public function do_imap_import(){
// .... Execută acțiuni când cron este declanșat ....
}
Deci, în acest exemplu, $this->do_imap_import();
este apelat la fiecare 30 de minute (presupunând că site-ul tău are suficient trafic).
Note
Este necesară o vizită pe pagină pentru ca jobul cron să se declanșeze la intervalele corecte.
Exemplu: Dacă ai programat o sarcină să ruleze la fiecare 30 de minute, dar nimeni nu vizitează site-ul timp de 4 ore, jobul cron nu va fi declanșat decât atunci când un vizitator accesează site-ul după acele 4 ore. Dacă chiar ai nevoie ca sarcina să fie executată la fiecare 30 de minute, este recomandat să configurezi un job cron real prin furnizorul de hosting pentru a accesa site-ul la intervalele dorite.
Joburile cron WordPress nu încetinesc site-ul!
Poate te întrebi ce se întâmplă dacă scriptul cron durează mult timp pentru a fi executat, vor trebui vizitatorii să aștepte până când scriptul se termină. Nu! Cum este posibil acest lucru? Dacă te uiți în fișierul wp-cron.php
, vei găsi linia
ignore_user_abort(true);
Este o configurare php.ini
care specifică că, dacă oprești încărcarea site-ului/scriptului, scriptul nu se va opri din execuție.
Dacă te uiți în fișierul wp-includes/cron.php
, vei găsi o linie similară cu aceasta:
wp_remote_post( $cron_url,
array('timeout' => 0.01,
'blocking' => false,
'sslverify' => apply_filters('https_local_ssl_verify', true)) );
Aceasta înseamnă că WordPress va aștepta doar 0.01 secunde pentru a declanșa execuția, apoi va întrerupe, dar, deoarece ai setat ignore_user_abort
la true
, scriptul va continua să ruleze. Această funcționalitate este un avantaj major pentru executarea scripturilor mari în cadrul joburilor cron WordPress.
Funcții disponibile pentru ajutor:

Acesta este un răspuns remarcabil de amplu, care, din câte văd, nu abordează întrebarea reală - și anume de ce toate sarcinile programate (inclusiv cele de bază) eșuează.

Acest răspuns are rolul de a ghida utilizatorul în direcția corectă pentru înțelegerea și programarea corectă a sarcinilor cron în WordPress.

Acest lucru m-a ajutat foarte mult să înțeleg programările cron în WordPress.

În ce moment ar trebui să faci "add_action('custom_imap_import', array($this, 'do_imap_import'))", presupunând o clasă de plugin? În constructor?

@codecowboy Da, sau undeva unde poate fi încărcat când cron-ul este pregătit să fie declanșat.

Core oferă deja un interval hourly
, așa că nu este nevoie să folosești 1_hours
.

Mai întâi, te rog confirmă dacă nu ai activat niciun plugin de caching. Plugin-urile de caching pot interfera cu job-urile cron deoarece vizitatorii tăi nu primesc o pagină live, ci o versiune cache a paginii.
Dacă ai un plugin de caching activat, poți alege una dintre paginile tale și să adaugi o excepție în setările plugin-ului de caching pentru acea pagină, astfel încât să nu fie niciodată cache-uită.
Apoi, va trebui să creezi manual un job cron (folosind cpanel dacă ești pe un mediu de hosting partajat sau din terminal dacă e un server VPS/dedicat) care să viziteze acea pagină la fiecare câteva minute.
Sper că te ajută!

WordPress Cron vă permite să programați sarcini, dar acestea vor fi executate doar dacă se face o cerere către site. Pentru fiecare cerere pe care o primește WordPress, acesta va verifica dacă există job-uri cron de procesat și, dacă da, va trimite o cerere asincronă către /wp-cron.php?doing_wp_cron
pentru a procesa job-ul. Dacă momentul programat al unui job trece fără ca să se facă vreo cerere, atunci procesul cron nu va fi pornit.
Deoarece puteți vedea și rula job-urile programate, este posibil să nu existe cereri care să declanșeze pornirea job-ului cron, mai ales dacă utilizați un plugin de caching. Cea mai bună opțiune pentru a descărca acest proces pe un program mai regulat este să dezactivați verificarea implicită din WordPress și să folosiți crontab
.
Mai întâi, pentru a dezactiva verificarea implicită (ceea ce poate ajuta puțin cu performanța pe partea clientului), adăugați următoarele în wp-config.php
:
// Dezactivează verificarea implicită pentru job-urile cron WordPress la încărcarea paginilor
define( 'DISABLE_WP_CRON', true );
Apoi, creați o sarcină care să acceseze pagina wp-cron.php
o dată pe minut pentru a procesa orice job-uri în fundal. Din linia de comandă, introduceți crontab -e
și apoi adăugați o linie care arată astfel:
*/1 * * * * /usr/bin/curl --silent http://example.com/wp-cron.php?doing_wp_cron=$(date +\%s.\%N) >/dev/null

Pentru cei care protejează site-ul lor (de dezvoltare) de accesul public, Autentificarea HTTP poate fi cauza pentru care WP Cron nu funcționează.
În cazul în care poate ajuta pe cineva, iată lista mea de lucruri pe care le-am făcut înainte de a identifica și a înțelege cerințele WP Cron:
- Am observat că evenimentele erau programate corect și puteau fi rulate folosind WP-CLI.
- Și, de asemenea, am observat că accesarea /wp-cron.php?doing_wp_cron prin browser a declanșat execuții, așa cum a fost sugerat în 13625.
- Documentația oficială a fost citită.
- M-am asigurat că DISABLE_WP_CRON nu era setat.
- Și am aflat că ALTERNATE_WP_CRON era o soluție de rezervă reușită, dar nesatisfăcătoare.
- Pentru a exclude bug-uri de regresie, am încercat să instalez câteva versiuni mai vechi de WordPress.
- Toate plugin-urile au fost dezactivate și tema a fost schimbată la cea implicită, pentru a izola problema.
- Oamenii au menționat termenul loopbacks, dar lipsind erori sau avertismente legate de acesta, l-am considerat irelevant.
- În cele din urmă, am găsit o întrebare cu un răspuns care explica efectiv cum să debug-ui cron.
- După ce am atașat Xdebug la wp_cron(), am putut vedea că execuția se termina în wp_remote_post(), care eșua clar.
Când am știut ce să caut, am găsit acest articol minunat despre cauzele problemelor WP Cron de Jeff Starr. La finalul acestuia, el face trimitere la plugin-ul său wp-cron-http-auth.

Verifică dacă DISABLE_WP_CRON nu este setat în configurarea ta.
Dacă nu este cazul, încearcă să dezactivezi toate plugin-urile (cu excepția core control - deși aș folosi wp-crontrol) și verifică dacă job-urile de bază funcționează. Dacă da, înseamnă că există o interferență din partea unui plugin.
De asemenea, încearcă să treci la o temă standard twentysomething.
Dacă niciuna dintre aceste soluții nu funcționează, probabil este o problemă de hosting.

Verifică orice plugin care ascunde WordPress.
Cum să vezi dacă acesta este problema?
- Navighează la http(s)://siteultau.com/wp-cron.php Ar trebui să vezi o pagină goală. Complet goală.
- În plus, trebuie să vezi în managerul de cron jobs o oră sub "Next execution":
(nu doar textul "In queue" - ci o oră specifică - pentru unele intrări "In queue" este OK uneori, dar dacă este singurul lucru pe care îl vezi -> cron-ul tău nu funcționează.)
+1. Nu crede orice plugin care "verifică dacă cron funcționează" - de exemplu, WP Cron status checker plugin a arătat că cron funcționează. Dar, de fapt, nu funcționa.
Concluzie: Dacă primești eroare 404 - atunci dezactivează a) nu doar pluginurile de caching, așa cum sugerează alții b) ci și orice pluginuri care ascund WordPress.
