309273ac8bed2fe5d2d79d092935809dd4a27293
[lhc/web/www.git] / www / ecrire / inc / genie.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2019 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 if (!defined('_ECRIRE_INC_VERSION')) {
14 return;
15 }
16
17 /**
18 * Gestion des tâches de fond
19 *
20 * Deux difficultés :
21 * - la plupart des hebergeurs ne fournissent pas le Cron d'Unix
22 * - les scripts usuels standard sont limites à 30 secondes
23 *
24 * Solution
25 * --------
26 * Toute connexion à SPIP s'achève par un appel (asynchrone si possible)
27 * à la fonction `cron()` qui appelle la fonction surchargeable `inc_genie_dist()`
28 *
29 * Sa définition standard ci-dessous prend dans une liste de tâches
30 * la plus prioritaire.
31 *
32 * Une fonction exécutant une tâche doit retourner un nombre :
33 * - nul, si la tache n'a pas à être effecutée
34 * - positif, si la tache a été effectuée
35 * - négatif, si la tache doit être poursuivie ou recommencée
36 *
37 * Elle recoit en argument la date de la dernière exécution de la tâche.
38 *
39 * On peut appeler cette fonction avec d'autres tâches (pour étendre SPIP)
40 * spécifiée par des fonctions respectant le protocole ci-dessus.
41 *
42 * On peut ajouter des tâches périodiques ou modifier la fréquence
43 * de chaque tâche et leur priorité en utilisant le pipeline
44 * `taches_generales_cron`.
45 *
46 * On peut également directement en déclarer avec la balise `genie` d'un paquet.xml
47 * de plugin, tel que `<genie nom="nom_de_la_tache" periode="86400" />`
48 *
49 * @package SPIP\Core\Genie
50 **/
51
52
53 /**
54 * Prévoit l'exécution de la tâche cron la plus urgente
55 *
56 * Les tâches sont dans un tableau `'nom de la tâche' => périodicité`
57 *
58 * Cette fonction exécute la tache la plus urgente, c'est à dire
59 * celle dont la date de dernière exécution + la périodicité est minimale.
60 *
61 * La date de la prochaîne exécution de chaque tâche est indiquée dans la
62 * table SQL `spip_jobs`
63 *
64 * La fonction exécutant la tâche est (généralement) un homonyme de préfixe "genie_".
65 * Le fichier homonyme du repertoire "genie/" est automatiquement lu
66 * et il est supposé définir cette fonction.
67 *
68 * @uses queue_add_job() Lorsqu'une tâche est à forcer
69 * @uses queue_schedule()
70 * @see taches_generales() Liste des tâches déclarées
71 *
72 * @param array $taches
73 * Tâches dont on force maintenant l'exécution le plus tôt possible.
74 * Sinon, prendra la tâche la plus prioritaire.
75 * @return
76 **/
77 function inc_genie_dist($taches = array()) {
78 include_spip('inc/queue');
79
80 if (_request('exec') == 'job_queue') {
81 return false;
82 }
83
84 $force_jobs = array();
85 // l'ancienne facon de lancer une tache cron immediatement
86 // etait de la passer en parametre a ing_genie_dist
87 // on reroute en ajoutant simplement le job a la queue, ASAP
88 foreach ($taches as $function => $period) {
89 $force_jobs[] = queue_add_job($function, _T('tache_cron_asap', array('function' => $function)),
90 array(time() - abs($period)), "genie/");
91 }
92
93 // et on passe la main a la gestion de la queue !
94 // en forcant eventuellement les jobs ajoute a l'instant
95 return queue_schedule(count($force_jobs) ? $force_jobs : null);
96 }
97
98 //
99 // Construction de la liste des taches.
100 // la cle est la tache,
101 // la valeur le temps minimal, en secondes, entre deux memes taches
102 // NE PAS METTRE UNE VALEUR INFERIEURE A 30
103 // les serveurs Http n'accordant en general pas plus de 30 secondes
104 // a leur sous-processus
105 //
106 // http://code.spip.net/@taches_generales
107 function taches_generales($taches_generales = array()) {
108
109 // verifier que toutes les taches cron sont planifiees
110 // c'est une tache cron !
111 $taches_generales['queue_watch'] = 3600 * 24;
112
113 // MAJ des rubriques publiques (cas de la publication post-datee)
114 // est fait au coup par coup a present
115 // $taches_generales['rubriques'] = 3600;
116
117 // Optimisation de la base
118 $taches_generales['optimiser'] = 3600 * 48;
119
120 // cache (chaque 10 minutes => 1/16eme du repertoire cache,
121 // soit toutes les 2h40 sur le meme rep)
122 $taches_generales['invalideur'] = 600;
123
124 // nouveautes
125 if (isset($GLOBALS['meta']['adresse_neuf']) and $GLOBALS['meta']['adresse_neuf']
126 and $GLOBALS['meta']['jours_neuf']
127 and ($GLOBALS['meta']['quoi_de_neuf'] == 'oui')
128 ) {
129 $taches_generales['mail'] = 3600 * 24 * $GLOBALS['meta']['jours_neuf'];
130 }
131
132 // maintenance (ajax, verifications diverses)
133 $taches_generales['maintenance'] = 3600 * 2;
134
135 // verifier si une mise a jour de spip est disponible (2 fois par semaine suffit largement)
136 $taches_generales['mise_a_jour'] = 3 * 24 * 3600;
137
138 return pipeline('taches_generales_cron', $taches_generales);
139 }
140
141 // Pas de fichier a part pour une fonction aussi petite:
142 // - elle peut retirer les fichiers perimes
143 // - elle fait appliquer le quota
144 // En cas de quota sur le CACHE/, nettoyer les fichiers les plus vieux
145 // http://code.spip.net/@genie_invalideur_dist
146 function genie_invalideur_dist($t) {
147
148 include_spip('inc/invalideur');
149 $encore = appliquer_quota_cache();
150
151 // si le cache est trop gonfle, redemander la main pour poursuivre
152 if ($encore) {
153 return (0 - $t);
154 }
155
156 return 1;
157 }
158
159 /**
160 * Une tâche périodique pour surveiller les tâches crons et les relancer si besoin
161 *
162 * Quand ce cron s'execute, il n'est plus dans la queue, donc il se replanifie
163 * lui même, avec last=time()
164 * avec une dose d'aleatoire pour ne pas planifier toutes les taches au meme moment
165 *
166 * @uses taches_generales()
167 * @uses queue_genie_replan_job()
168 *
169 * @return int
170 */
171 function genie_queue_watch_dist() {
172 static $deja_la = false;
173 if ($deja_la) {
174 return;
175 } // re-entrance si l'insertion des jobs echoue (pas de table spip_jobs a l'upgrade par exemple)
176 $deja_la = true;
177 $taches = taches_generales();
178 $programmees = sql_allfetsel('fonction', 'spip_jobs', sql_in('fonction', array_keys($taches)));
179 $programmees = array_map('reset', $programmees);
180 foreach ($taches as $tache => $periode) {
181 if (!in_array($tache, $programmees)) {
182 queue_genie_replan_job($tache, $periode, time() - round(rand(1, $periode)), 0);
183 }
184 }
185 $deja_la = false;
186
187 return 1;
188 }
189
190 /**
191 * Replanifier une tache periodique
192 *
193 * @param string $function
194 * nom de la fonction a appeler
195 * @param int $period
196 * periodicite en secondes
197 * @param int $last
198 * date du dernier appel (timestamp)
199 * @param int $time
200 * date de replanification
201 * si null calculee automaitquement a partir de $last et $period
202 * si 0 = asap mais on n'insere pas le job si deja en cours d'execution
203 * @param int $priority
204 * priorite
205 * @return void
206 */
207 function queue_genie_replan_job($function, $period, $last = 0, $time = null, $priority = 0) {
208 static $done = array();
209 if (isset($done[$function])) {
210 return;
211 }
212 $done[$function] = true;
213 if (is_null($time)) {
214 $time = time();
215 if ($last) {
216 $time = max($last + $period, $time);
217 }
218 }
219 if (!$last) {
220 $last = $time - $period;
221 }
222 spip_log("replan_job $function $period $last $time $priority", 'queue');
223 include_spip('inc/queue');
224 // on replanifie un job cron
225 // uniquement si il n'y en a pas deja un avec le meme nom
226 // independament de l'argument
227 queue_add_job($function, _T('tache_cron_secondes', array('function' => $function, 'nb' => $period)), array($last),
228 "genie/", 'function_only', $time, $priority);
229 }