Salve a tutti,
oggi volevo parlarvi di un comando non sempre presente sui nostri piani di hosting, ovvero il CronTab!
Ora supponendo che abbiate staccato gli occhi dalle due signorine programmatrici, vi starete chiedendo: (chi già lo sa può continuare a guardare la foto!
) Ma cos’è CronTab?
Citando Wikipedia:
Nei sistemi operativi Unix e Unix-like, il comando
crontabconsente lo scheduling di comandi, ovvero consente di registrarli presso il sistema affinché siano poi mandati in esecuzione periodicamente. Lo stesso nome “crontab” si riferisce anche ai file utilizzati dal comando crontab come “registri” dei comandi “schedulati”.Generalmente,
crontabusa un demone, chiamatocrond, che è costantemente in esecuzione in backgroundcrontabviene chiamato cronjob
In Cosa può essere utile un comando del genere con PHP?
Ad esempio quando volete che una volta al giorno, venga controllato se ci sono utenti del vostro sito in php, che si sono iscritti, ma non hanno confermato la richiesta…
Oppure ad esempio, se dobbiamo eliminare dal server dei file molto vecchi ad intervalli di tempo regolari.
Però cosa fare se il nostro piano di Hosting non ci permette di aver il comando CronTab?
Per aggirare, almeno in parte, il problema… ho creato questa classe in php che ci permette di eseguire degli script php, ad intervalli regolari definiti in minuti al momento della chiamata.
La Classe e le sue Funzioni:
Cronjob Simulator, permette di eseguire un file php ad intervalli regolari definiti in minuti nella chiamata alla funzione. Il funzionamento è semplicissimo:
Vengono usate 2 funzioni di PHP che permettono di far andare i comandi anche se l’utente ha chiuso il browser o annullato la richiesta, e un ciclo continuo che eseguirà i compiti assegnati.
Veniamo al Codice della Classe: (PHP) libs/cronjob.class.inc.php
< ?php
/***********************************************
* CronJob Simulator *
* by *
* Stefano V. *
* info@svsoftwares.org *
***********************************************/
class CronjobSimulator {
// true continua, false ferma il ciclo
private $flag = true;
// avvia il cron e setta l'intervallo
public function cronFile($file, $intervallo = 1)
{
// ignora se l'utente annulla
ignore_user_abort();
// lo script va all'infinito
set_time_limit(0);
// intervallo di tempo ogni quanto far eseguire il file
$interval = 60 * $intervallo;
// legge i log e dedide se avviare il ciclo
if(!$this->checkLoop($file))
{
// cicla
do
{
// se esiste il file da includere
if(file_exists($file))
{
// setta la flag a true
$this->flag = true;
// include il file da eseguire
include($file);
// scrive il log
$this->creaLog($file, $intervallo);
}
else
{
// setta la flag a false.
$this->flag = false;
}
// ferma intervallo
sleep($interval);
}
while($this->flag);
}
else
{
return false;
}
}
private function creaLog($file, $intervallo)
{
// apro il log
$fl = fopen("cronjob.log", "w");
// scrivo il log
fwrite($fl, "Classe CronJob Simulator by S.V Design <info @sv-design.org>\r\n");
fwrite($fl, "Stato: Cronfile eseguito.\r\n");
fwrite($fl, "Intervallo: $intervallo minuti\r\n");
fwrite($fl, "File eseguito: $file.\r\n");
fwrite($fl, "Timestamp attuale: ".mktime()."\r\n");
fwrite($fl, "Ora Attuale: ".date("H.i")."\r\n");
// chiude il file
fclose($fl);
}
private function checkLoop($file)
{
// se esiste il file da eseguire
if(file_exists($file))
{
// se esiste il log
if(file_exists("cronjob.log"))
{
// legge le righe
$righe = file("cronjob.log");
// intervallo
$tempo = $righe[2];
// orario
$ora = $righe[4];
// se c'è un numero nell'intervallo
if(eregi("[0-9]+", $tempo, $reg))
{
// lo legge
$tempo = $reg[0];
// se c'è un numero nell'ora
if(eregi("[0-9]+", $ora, $req))
{
// lo legge
$ora = $req[0];
// ora attuale
$adesso = mktime();
// differenza tra l'ultima esecuzione e adesso
$differenza = $adesso - $ora;
// minuti equivalenti alla differenza
$minuti = date("i", $differenza);
if($minuti < = $tempo)
{
// true = blocca ciclo
return true;
}
else
{
// false = avvio ciclo
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return true;
}
}
}
?>
Ora creiamo uno script (sempre php) che conterrà i dati da eseguire periodicamente dal Crontab Simulato.
Nell’esempio riportato il file è chiamato “example_script.php” e creerà sul server un file vuoto chiamato “cron_” seguito dal timestamp.
$fp = fopen("cron_".mktime().".txt", "w");
fwrite($fp, " ");
fclose($fp);
Il passo successivo è quello di creare un file dal quale richiameremo la classe e lo script.
Il questo esempio il file si chiama “esempio.php” ed esegue la classe ogni minuto:
include 'libs/cronjob.class.inc.php';
$cron = new CronjobSimulator();
if( !$cron->cronFile("example_script.php", 1) )
print("Ciclo in esecuzione o file inesistente!");
Come possiamo vedere, richiamo la funzione cronFile della classe, passando come parametro lo script che contiene i dati da eseguire, e l’intervallo in minuti tra un’esecuzione e l’altra.
Che succede se avvio 2 volte il file esempio? Si creano 2 processi sul server?
No, assolutamente.
La classe ha al suo interno una funzione che scrive un log (“cronjob.log“) nel quale memorizza, l’ultima esecuzione, e l’intervallo… e da questi ne deduce se il ciclo è ancora in esecuzione.
Finchè il ciclo sarà in esecuzione, non sarà inviata una seconda richiesta al server.
Come fermo il Ciclo?
Purtroppo il ciclo non si può fermare perchè rimane in memoria al server, finchè il nostro provider non decide per qualche motivo di riavviare Apache.
In alternativa, basterà cancellare lo script da eseguire, in questo modo la classe controllerà che lo script non esiste più ed il ciclo verrà fermato!
Limitazioni?
Si, purtroppo. Siccome il file di log è chiamato cronjob.log, non si possono avere più comandi in una stessa cartella, altrimenti salterebbe il meccanismo di controllo in caso di ri-esecuzione dello script.
Nel caso volessimo eseguire più comandi ad intervalli differenti, dovremo per forza spostarci in un altra cartella.
Questa limitazione verrà corretta nella prossima versione che rilascerò non appena avrò tempo!
File dell’esempio da scaricare: DOWNLOAD CRONJOB SIMULATOR
Popolarità: 2%
Articoli correlati:


[...] l’articolo completo: http://www.sv-design.org/blog/php-cronjob-simulator/ Share and Enjoy: These icons link to social bookmarking sites where readers can share and [...]
[...] seguente link vi rimanda all’articolo scritto su S.V. Design dove è presente una classe che ci consentirà [...]
Ciao solo ora mi sono interessato al fatto di trovare un alternativa all’utilizzo del cron visto che, da quanto ho letto (ma potrei anche aver sbagliato), su altervista non è possibile accedere a questa funzioncina carina di linux. Ho letto il tuo codice e, come giustamente dici anche te, non è possibile killare l’esecuzione dello script a meno che, la funzione da te creata non restituisca al chiamante un FALSE. Ma questo comunque non fermerebbe, se non sbaglio, l’esecuzione, ma forse sarebbe più efficace, l’utilizzo di throw per generare un eccezione, che se non viene gestita, causerebbe l’interruzione “anomala” dello script. Sbaglio?
Salutoni. Daniele
Si avevo pensato a questa soluzione, e volendo la si può integrare…
Però se non erro Throw e tutta la struttura try-throw-catch è stata introdotta da PHP5…
Ho voluto rendere più compatibile la mia classe, anche a discapito di un piccolo spreco di risorse.
Infatti, le istruzioni saranno in example_script.php, mentre il file ciclato all’infinito è esempio.php, ovvero un file di poche righe, direi quasi trascurabili se non si abusa di questa funzione.
Potrei anche inserire un controllo sulla versione di PHP e quindi decidere se usare Throw o la Flag! Quando avrò un pò di tempo farò questa modifica.
Grazie per il tuo parere!
Tramite Cronjob Simulator vorrei caricare una pagina web ogni tre ore per consentire l’aggiornamento delle notizie. Come devo impostare l’example_script.php? Grazie.
dentro example_script.php tu metti il codice della pagina che serve ad aggiornare le notizie, e poi tramite un’altra pagina…ad esempio “esempio.php” metti il codice:
include ‘libs/cronjob.class.inc.php’;
$cron = new CronjobSimulator();
if( !$cron->cronFile(“example_script.php”, 180) )
print(“Ciclo in esecuzione o file inesistente!”);
dove 180 sono i minuti di intervallo prima di aggiornare… 180 minuti = 3 ore
Come codice da inserire in example_script.php va bene questo:
? Scusami, ma non sono molto esperto. Grazie.
E’ sparito il codice, forse a causa dei tag php. Provo a riscriverlo senza i tag di apertura e chiusura php, sperando che così si veda: header (“location: http://www.esempio.com/“);
Gabriele, se quello ke cerchi di fare è aggiornare la pagina all’utente ke la visualizza ogni 3 ore, nn puoi usare cron e header, xkè cron lavora in background quindi nn aggiornerà nulla all’utente…
Per fare quello che chiedi devi usare Javascript con la funzione setInterval()
A me serve un aggiornamento lato server e non lato client. La pagina da aggiornare non è visualizzabile dagli utenti, ma serve per l’update e la produzione di un file .xml che alimenta poi tramite magpie le pagine viste dagli utenti. In questo caso, il codice che ti ho esposto con header va bene?
No, non va bene header!
Header appunto setta l’header da visualizzare sul browser dell’utente, quindi lato server non fa nulla che a te serva!
In example_script.php devi inserire il codice ke crea e/o aggiorna l’xml! Tutto qui
Scusami, ma non essendo un esperto, quello che per te è scontato per me è difficile da capire. Ciò che aggiorna l’xml non è un codice, ma una pagina web, che dovrebbe essere richiamata ad intervalli di tempo stabiliti (es.: 3 ore) per l’update. Ho provato ad inserire in example_script.php semplicemente l’indirizzo web della pagina, senza header, ma non funziona. E’ possibile con il tuo script richiamare ad intervalli di tempo prefissati una pagina web, come fa il cron, che però il mio provider non mi mette a disposizione? Se sì, qual è il codice corretto da inserire in example_script.php? Grazie.
Allora, ti spiego:
quando tu nella pagina esempio.php usi questo codice:
if( !$cron->cronFile(”example_script.php”, 180) )
print(”Ciclo in esecuzione o file inesistente!”);
dici di kiamare la PAGINA WEB dal nome example_script.php ogni 180 minuti…
Se il file che aggiorna le notizie da te si chiama pippo.php, ti basta cambiare il codice richiamando quel file, ovvero così:
if( !$cron->cronFile(”pippo.php”, 180) )
print(”Ciclo in esecuzione o file inesistente!”);
Il tuo suggerimento in linea di massima è giusto. Infatti, eseguendo il tuo script con una pagina web contenente un codice semplice (es.: prova.php), il ciclo si ripete indefinitamente. Purtroppo, nel mio caso la pagina web contiene un codice piuttosto complesso e, per qualche motivo che non riesco a individuare, dopo la prima esecuzione corretta (con relativo rilascio del file di log), alla seconda esecuzione compare sul browser la pagina web richiamata da example_script.php e il ciclo di interrompe. Comunque, grazie mille per il tuo aiuto.
Facciamo così: mandami la pagina da cui richiami cronjob e la pagina che esegui zippate a: info [at] sv-design.org
E cerco di capire se sbagli qualcosa o se è un problema della mia classe!
Ti ho inviato la email con i file necessari per l’esecuzione delle prove. Fammi sapere se ti è arrivata e se è tutto chiaro.
Allora, ho letto la mail e il codice…
Ovviamente prova 1 è solo una prova e non andrà messa online giusto? xkè 2 cron nella stessa cartella, come ho scritto nel post, non funzionano perchè vanno in conflitto tra loro…
Poi altra cosa che ho notato all’inizio dello script ke lanci è:
set_time_limit (300); # allow script to run longer
In questo modo setti il limite di esecuzione, mentre nello script del cron è gia impostato su infinito (set_time_limit(0);) quindi prova a togliere questo…
Cmq per evitare conflitti dall’esecuzione del cron col browser aperto sulla pagina ciclata, ti consiglio di avviare la pag di cron (dopo aver fatto la modifica ke ti ho detto prima) e chiudere il browser (tanto gira in background)..
Prova e fammi sapere!
Purtroppo, niente da fare. Ho tenuto un solo cron nella cartella, ho eliminato il set_time_limit dallo script lanciato (e mantenuto il set_time_limit (0); nel tuo script), ho avviato il cronjob e chiuso il browser, ma il risultato è sempre lo stesso: dopo la prima esecuzione corretta, il ciclo si interrompe.
Ciao, io ho un problemino che non so da cosa puo’ dipendere. Ho provato la tua classe. Di php non ne sapevo niente ma sto iniziando a prenderci gusto. Ho notato una cosa. Lo script funziona perfettamente quando lo provo sulla mia macchina. Ma quando lo metto sull’host pare funzionare un tempo “random” e poi e’ come se si bloccasse il ciclo. Hai suggerimenti? Potrebbe essere l’host che ha impostato qualche cosa che interferisce con la tua classe?
Ciao, allora:
a quanto pare non tutti gli hoster permettono il “set_time_limit(0)”…
Alcuni bloccano questa funzione e settano un tempo loro per evitare che i processi vadano a sovraccaricare e quindi crashare il server…
Puoi provare ad impostare al posto di quel comando, il codice: ini_set(‘max_execution_time’, 0);
Grazie mille per la risposta tempestiva. Effettivamente immaginavo fosse un vincolo di alcuni host. Per ora il metodo che mi hai suggerito pare funzionare, ma dovrei testarlo per un po’ di tempo. Anche prima l’esecuzione era abbastanza casuale.
Per ora pero’ sembra girare bene.
Grazie a te!
Quando sarai certo che il sistema funziona perfettamente, se avrai un pò di tempo, postami qui la tua conferma… così correggo lo script!
Ciao Stefano,
grazie per la tua classe, mi è utilissima, ho però un problema parte la prima volta e poi si ferma, ho provato anche con ini_set(’max_execution_time’, 0); ma nulla funziona solo quando la richiamo da browser. del tuo codice non ho cambiato nulla se non il tempo impostato a 1 e una aggiunta in example_script.php mettendo una query per scrivere la data e l’ora in un DB mysql. Hai qualche idea del perchè si blocca? Grazie
Ciao Paolo, purtroppo alcuni host bloccano quel parametro a poche ore, o addirittura miunti. prova a chiedere al tuo servizio hosting se hanno impostato qualche limite.
Ok grazie,
proverò a chiedere, l’unica cosa è che avendo impostato 1 lo script si dovrebbe ripete ogni minuto quindi il loro blocco è addirittura inferiore al minuto o addirittura immediato!! Grazie comunque e buon lavoro.
Grazie. Fammi sapere, cosi se il problema è dovuto al mio script lo correggerò subito.
Ciao Stefano, avevo fatto io un errore il tuo script funziona benissimo, ho solo una domanda, ho fatto delle prove su “$interval = 60 * $intervallo;” ho notato questo, se metto ad 1 mi cicla ogni secondo se metto a 15 ogni 15 secondi ma se metto 120 mi cicla ogni 10minuti e non 2 minuti. Mi sai dire dove sbaglio? Grazie
Paolo.
$interval = 60 * $intervallo
Per 2 minuti lascia il codice com’è e passa l’intervallo = 2
Se poi vuoi i secondi allora al posto di 60 metti 1, e l’intervallo che passerai alla funzione sarà in secondi…
Ciao, volevo capire una cosa… ho un sito all’interno delle cui pagine mostro a video il numero di utenti online. Volevo chiederti se è corretto utilizzare le tue classi per far eseguire ongi tot minuti lo script che conta e mostra gli utenti online. Questo perchè attualmente lo script è nella pagina immediatamente dopo il login e non nelle altre… ergo lo script viene eseguito solo all’ingresso dell’utente e poi mai piu, con il risultato di mostrare offline un utente che invece magari è online. Una soluzione sarebbe di includere lo script in tutte le pagine ma con un cronjob sarebbe molto meglio… penso.
Fagiano non credo sia un utilizzo proprio della classe, perchè per tenere traccia degli utenti online un modo meno dispendioso di risorse potrebbe essere:
Salvare l’id di sessione al login dell’utente nel database insieme al timestamp, e ad ogni pagina che visita l’utente, ad inizio pagina metti un codice ke aggiorna quell’id di sessione spostando il timestamp di 5 minuti ad esempio, dopodiche un codice ke cancella tutte le sessioni del database dove il timestamp attuale, meno quello di creazione (o aggiornamento) è maggiore di 5 minuti x esempio…
In questo modo x contare gli utenti online ti basta fare un COUNT(*) della tabella delle sessioni, e se oltre all’id sessione e data, segni anche il nome utente, puoi avere anche la lista degli utenti online ed impedire il login se è gia stato effettuato da un altro pc cn lo stesso account…
Come vedi il cronjob non serve in questo caso, al massimo potresti applicarlo per la cancellazione delle sessioni ogni 5 minuti…
Io ho installato in locale Easy PHP per simulare un server sul mio pc (winXP) e fare prove in locale con Wp e Joomla: pensi che lo script possa funzionare in qualche modo anche in locale?
A spanne direi di no, perchè suppongo necessiti di un ambiernte linux: io potrei avercelo virtualizzato sul mio pc (virtualbox) con un paio di distro a scelta(Slax, DSL, Slackware, Puppy);
se facessi eseguire cronjob al Linux (mi serve per un plugin Wp da eseguire con frequenza programmata) potreebbe funzionare???
Grazie per l’attenzione,
Michelangelo
Ciao michelangelo,
installando EasyPHP hai praticamente installato il webserver APACHE, quindi la classe funziona perfettamente senza necessità di installare una distro di linux
2 osservazioni:
alla riga 041 io usere file_get_contents($file) per 3 ragioni:
1) utilizzabile per interrogare files in altri hosting,
2) posso usare dei parametri in get,
3) se include il file lo scope di tutte le variabili dichiarate sarà all’ interno della classe… con file get_contents invece lo scope delle variabili rimane globale
Mirko
inotre metterei un callback arbitrario sull’ esecuzione del file schedulato, in maniera tale che possa far seguire ad un determinato evento , un’ azione arbitraria mia…
tipo
$istance->addEvent(‘miofile.php’,’send_mail’);
e la funzione send_mail
function send_mail(){
…code…
}
Mirko