Il y a 2 manières de déclarer une tâche et sa périodicité :
- avec le pipeline
taches_generales_cron
. - avec la balise
<genie>
dans le fichier paquet.xml
.
Avec le pipeline taches_generales_cron
Pour déclarer une tâche, on peut indiquer son nom et sa périodicité en secondes via le pipeline taches_generales_cron
:
function monplugin_taches_generales_cron($taches){
$taches['nomdelatache'] = 24*3600; // tous les jours
return $taches;
}
Cette tâche sera appelée au moment venu. Les traitements sont placés dans un fichier du répertoire genie/
, homonyme à la tâche (nomdelatache.php
) et disposant d’une fonction genie_nomdelatache_dist()
.
La fonction reçoit en argument la date à laquelle s’est réalisé le dernier traitement de cette tâche. Elle doit retourner un nombre :
- nul, si la tâche n’a rien à faire
- positif, si la tâche a été traitée
- négatif, si la tâche a commencé, mais doit se poursuivre. Cela permet d’effectuer des tâches par lots (pour éviter des timeout sur les exécutions des scripts PHP à cause de traitements trop longs). Dans ce cas là, la valeur absolue du nombre négatif indiqué doit être le timestamp de la dernière exécution de la tâche à partir duquel doit s’appliquer le délai spécifié dans le pipeline
taches_generales_cron
pour définir le moment de la prochaine exécution.
Exemple
Cet exemple est simple, issu des tâches de « maintenance » de SPIP, dans le fichier genie/maintenance.php
, puisqu’il exécute des fonctions et renvoie toujours 1
, indiquant que l’action a été réalisée.
// Diverses taches de maintenance
function genie_maintenance_dist ($t) {
// (re)mettre .htaccess avec deny from all
// dans les deux repertoires dits inaccessibles par http
include_spip('inc/acces');
verifier_htaccess(_DIR_ETC);
verifier_htaccess(_DIR_TMP);
// Verifier qu'aucune table n'est crashee
if (!_request('reinstall'))
verifier_crash_tables();
return 1;
}
Avec la balise <genie>
On peut également déclarer la tâche dans un plugin en ajoutant dans le fichier paquet.xml
une balise <genie>
avec les attributs "nom" et "periode".
La tâche déclarée sera alors appelée avec la périodicité indiquée.
Attention
Le traitement doit également être placé dans un fichier du répertoire genie/
, mais par rapport au nom de fichier lorsque la tâche est déclarée par le pipeline, le nom du fichier doit être préfixé par le préfixe du plugin : prefixeplugin_nomdelatache.php
.
De même, il faut ajouter le préfixe du plugin dans le nom de la fonction : genie_prefixeplugin_nomdelatache_dist()
.
Exemple
Dans le plugin ipset, le fichier paquet.xml
contient la ligne suivante :
<genie nom="accessip" periode="86400" />
.
Et le fichier genie/ipset_accessip.php
définit la fonction
genie_ipset_accessip_dist($t)
.
Exemple
Programmer une tâche cron sans dérive horaire
L’API pour les tâches cron permet de facilement déclarer des périodicités pour les déclenchements, mais il est plus délicat de déclarer des heures de déclenchements fixes chaque jour.
La date du prochain cron dépend en effet de la valeur renvoyée par le génie et de la déclaration de délais faite dans le pipeline taches_generales_cron... mais elle dépend aussi de la fréquentation du site, car lorsque le délai est passé et sur un site peu fréquenté, il faut parfois attendre de nombreuses minutes avant que le cron ne s’exécute. Sans mesure particulière, ces délais s’ajoutent peu à peu, et une tâche de périodicité 24h, lancée un jour à minuit, se trouve exécutée à midi quelques semaines ou mois plus tard.
Voici une fonction qui aide à viser une heure fixe pour le déclenchement d’une tâche chaque jour J à 23h, sans dériver, car elle évite en effet les dérives progressives en recalant le déclenchement chaque jour. Par contre, elle ne garantit pas que le déclenchement se fasse précisément à l’heure programmée.
– Comme c’est un lancement quotidien, le délai pour ce cron est de 24h.
– Dans le code, la constante CHAQUE_JOUR_H_DEBUT
vaut l’heure de déclenchement désiré, ici 23h.
Exemple : define ('CHAQUE_JOUR_H_DEBUT', 23);
Le jour suivant J+1, un nouveau lancement doit se faire à la même heure HH visée environ, sans que le décalage de J ne s’ajoute au nouveau décalage de J+1.
– Le délai de déclenchement réel de la tâche étant impossible à contrôler précisément, on ne peut savoir quelle heure il est quand le cron se déclenche réellement : il peut être 23h01, ou 23h12, donc le jour même... mais il peut être minuit 30, c’est à dire le lendemain. Le code tient compte de ces différentes possibilités.
/**
* Éviter la dérive horaire d'un cron de périodicité 24h en calculant,
* en fonction de son horaire d'exécution, la valeur de retour d'un génie
* permettant sa prochaine exécution à la même heure le lendemain
* ou le jour même si le cron a trop duré ou pris beaucoup de retard.
*
* @param int $h_debut_cejour heure visé le jour même. Exemple : 23
* @return float|int
*/
function genie_vise_heure(int $h_debut_cejour) {
// timestamp de l'heure visée (23h) du jour courant
$h_debut_cejour = mktime($h_debut_cejour, 0, 0);
$now = time();
// S'il est plus de 23h mais moins que minuit, c'est donc dans le passé
if ($now > $h_debut_cejour) {
// Le prochain cron devra se relancer "demain"
// On veut donc garder le délai normal (de 24h) spécifié par le pipeline
// par rapport à $h_debut_cejour reçu = 23h ce soir
return (0-$h_debut_cejour);
}
// Mais si on est avant $h_debut_cejour, il est par exemple 1h ou 5h du mat,
// C'est parceque le cron a mis très longtemps à se déclancher (gros délai)
// ou à s'exécuter.
// Le 23h visé par $j_debug_cejour, c'est le jour même, dans 22 ou 18h.
// Pour obtenir l'exécution du prochain cron à cette heure visée,
// il faut renvoyer « 23h hier ».
return (0 - ($h_debut_cejour - 24 * 3600));
}