fb9d0f58d5a6ad2884127c8b2f357d0d069e5fbb
[lhc/web/www.git] / www / ecrire / inc / invalideur.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2017 *
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 /**
14 * Gestion du cache et des invalidations de cache
15 *
16 * @package SPIP\Core\Cache
17 **/
18
19 if (!defined('_ECRIRE_INC_VERSION')) {
20 return;
21 }
22
23 include_spip('base/serial');
24
25 /** Estime la taille moyenne d'un fichier cache, pour ne pas les regarder (10ko) */
26 if (!defined('_TAILLE_MOYENNE_FICHIER_CACHE')) {
27 define('_TAILLE_MOYENNE_FICHIER_CACHE', 1024 * 10);
28 }
29 /**
30 * Si un fichier n'a pas été servi (fileatime) depuis plus d'une heure, on se sent
31 * en droit de l'éliminer
32 */
33 if (!defined('_AGE_CACHE_ATIME')) {
34 define('_AGE_CACHE_ATIME', 3600);
35 }
36
37 /**
38 * Calcul le nombre de fichiers à la racine d'un répertoire ainsi qu'une
39 * approximation de la taille du répertoire
40 *
41 * On ne calcule que la racine pour pour aller vite.
42 *
43 * @param string $dir Chemin du répertoire
44 * @param string $nb_estim_taille Nombre de fichiers maximum pour estimer la taille
45 * @return bool|array
46 *
47 * - false si le répertoire ne peut pas être ouvert
48 * - array(nombre de fichiers, approximation de la taille en octet) sinon
49 **/
50 function nombre_de_fichiers_repertoire($dir, $nb_estim_taille = 20) {
51 $taille = 0; // mesurer la taille de N fichiers au hasard dans le repertoire
52 $nb = $nb_estim_taille;
53 if (!$h = opendir($dir)) {
54 return false;
55 }
56 $total = 0;
57 while (($fichier = @readdir($h)) !== false) {
58 if ($fichier[0] != '.' and !is_dir("$dir/$fichier")) {
59 $total++;
60 if ($nb and rand(1, 10) == 1) {
61 $taille += filesize("$dir/$fichier");
62 $nb--;
63 }
64 }
65 }
66 closedir($h);
67
68 return array($total, $taille ? $taille / ($nb_estim_taille - $nb) : _TAILLE_MOYENNE_FICHIER_CACHE);
69 }
70
71
72 /**
73 * Évalue approximativement la taille du cache
74 *
75 * Pour de gros volumes, impossible d'ouvrir chaque fichier,
76 * on y va donc à l'estime !
77 *
78 * @return int Taille approximative en octets
79 **/
80 function taille_du_cache() {
81 # check dirs until we reach > 500 files
82 $t = 0;
83 $n = 0;
84 $time = isset($GLOBALS['meta']['cache_mark']) ? $GLOBALS['meta']['cache_mark'] : 0;
85 for ($i=0; $i < 256; $i++) {
86 $dir = _DIR_CACHE.sprintf('%02s', dechex($i));
87 if (@is_dir($dir) and is_readable($dir) and $d = opendir($dir)) {
88 while (($f = readdir($d)) !== false) {
89 if (preg_match(',^[[0-9a-f]+\.cache$,S', $f) and $a = stat("$dir/$f")) {
90 $n++;
91 if ($a['mtime'] >= $time) {
92 if ($a['blocks'] > 0) {
93 $t += 512*$a['blocks'];
94 } else {
95 $t += $a['size'];
96 }
97 }
98 }
99 }
100 }
101 if ($n > 500) {
102 return intval(256*$t/(1+$i));
103 }
104 }
105 return $t;
106 }
107
108
109 /**
110 * Invalider les caches liés à telle condition
111 *
112 * Les invalideurs sont de la forme 'objet/id_objet'.
113 * La condition est géneralement "id='objet/id_objet'".
114 *
115 * Ici on se contente de noter la date de mise à jour dans les metas,
116 * pour le type d'objet en question (non utilisé cependant) et pour
117 * tout le site (sur la meta `derniere_modif`)
118 *
119 * @global derniere_modif_invalide
120 * Par défaut à `true`, la meta `derniere_modif` est systématiquement
121 * calculée dès qu'un invalideur se présente. Cette globale peut
122 * être mise à `false` (aucun changement sur `derniere_modif`) ou
123 * sur une liste de type d'objets (changements uniquement lorsqu'une
124 * modification d'un des objets se présente).
125 *
126 * @param string $cond
127 * Condition d'invalidation
128 * @param bool $modif
129 * Inutilisé
130 **/
131 function suivre_invalideur($cond, $modif = true) {
132 if (!$modif) {
133 return;
134 }
135
136 // determiner l'objet modifie : forum, article, etc
137 if (preg_match(',["\']([a-z_]+)[/"\'],', $cond, $r)) {
138 $objet = objet_type($r[1]);
139 }
140
141 // stocker la date_modif_$objet (ne sert a rien pour le moment)
142 if (isset($objet)) {
143 ecrire_meta('derniere_modif_' . $objet, time());
144 }
145
146 // si $derniere_modif_invalide est un array('article', 'rubrique')
147 // n'affecter la meta que si un de ces objets est modifie
148 if (is_array($GLOBALS['derniere_modif_invalide'])) {
149 if (in_array($objet, $GLOBALS['derniere_modif_invalide'])) {
150 ecrire_meta('derniere_modif', time());
151 }
152 } // sinon, cas standard, toujours affecter la meta
153 else {
154 ecrire_meta('derniere_modif', time());
155 }
156 }
157
158
159 /**
160 * Purge un répertoire de ses fichiers
161 *
162 * Utilisée entre autres pour vider le cache depuis l'espace privé
163 *
164 * @uses supprimer_fichier()
165 *
166 * @param string $dir
167 * Chemin du répertoire à purger
168 * @param array $options
169 * Tableau des options. Peut être :
170 *
171 * - atime : timestamp pour ne supprimer que les fichiers antérieurs
172 * à cette date (via fileatime)
173 * - mtime : timestamp pour ne supprimer que les fichiers antérieurs
174 * à cette date (via filemtime)
175 * - limit : nombre maximum de suppressions
176 * @return int
177 * Nombre de fichiers supprimés
178 **/
179 function purger_repertoire($dir, $options = array()) {
180 if (!is_dir($dir) or !is_readable($dir)) {
181 return;
182 }
183 $handle = opendir($dir);
184 if (!$handle) {
185 return;
186 }
187
188 $total = 0;
189
190 while (($fichier = @readdir($handle)) !== false) {
191 // Eviter ".", "..", ".htaccess", ".svn" etc.
192 if ($fichier[0] == '.') {
193 continue;
194 }
195 $chemin = "$dir/$fichier";
196 if (is_file($chemin)) {
197 if ((!isset($options['atime']) or (@fileatime($chemin) < $options['atime']))
198 and (!isset($options['mtime']) or (@filemtime($chemin) < $options['mtime']))
199 ) {
200 supprimer_fichier($chemin);
201 $total++;
202 }
203 } else {
204 if (is_dir($chemin)) {
205 $opts = $options;
206 if (isset($options['limit'])) {
207 $opts['limit'] = $options['limit'] - $total;
208 }
209 $total += purger_repertoire($chemin, $opts);
210 if (isset($options['subdir']) && $options['subdir']) {
211 spip_unlink($chemin);
212 }
213 }
214 }
215
216 if (isset($options['limit']) and $total >= $options['limit']) {
217 break;
218 }
219 }
220 closedir($handle);
221
222 return $total;
223 }
224
225
226 //
227 // Methode : on prend un des sous-repertoires de CACHE/
228 // on considere qu'il fait 1/16e de la taille du cache
229 // et on le ratiboise en supprimant les fichiers qui n'ont pas
230 // ete sollicites dans l'heure qui vient de s'ecouler
231 //
232 // http://code.spip.net/@appliquer_quota_cache
233 function appliquer_quota_cache() {
234
235 $encore = false;
236
237 $tour_quota_cache = intval(1 + $GLOBALS['meta']['tour_quota_cache']) % 16;
238 ecrire_meta('tour_quota_cache', $tour_quota_cache);
239
240 $l = dechex($tour_quota_cache);
241 $dir = sous_repertoire(_DIR_CACHE, $l);
242 list($nombre, $taille) = nombre_de_fichiers_repertoire($dir);
243 $total_cache = $taille * $nombre;
244 spip_log("Taille du CACHE estimee ($l): "
245 . (intval(16 * $total_cache / (1024 * 1024 / 10)) / 10) . ' Mo', 'invalideur');
246
247 // Nombre max de fichiers a supprimer
248 if ($GLOBALS['quota_cache'] > 0
249 and $taille > 0
250 ) {
251 $trop = $total_cache - ($GLOBALS['quota_cache'] / 16) * 1024 * 1024;
252 $trop = 3 * intval($trop / $taille);
253 if ($trop > 0) {
254 $n = purger_repertoire(
255 $dir,
256 array(
257 'atime' => time() - _AGE_CACHE_ATIME,
258 'limit' => $trop,
259 'subdir' => true // supprimer les vieux sous repertoire de session (avant [15851])
260 )
261 );
262 spip_log("$dir : $n/$trop caches supprimes [taille moyenne $taille]", 'invalideur');
263 $total_cache = intval(max(0, (16 * $total_cache) - $n * $taille) / (1024 * 1024) * 10) / 10;
264 spip_log("cache restant estime : $total_cache Mo, ratio " . $total_cache / $GLOBALS['quota_cache'], 'invalideur');
265
266 // redemander la main pour eviter que le cache ne gonfle trop
267 // mais pas si on ne peut pas purger car les fichiers sont trops recents
268 if (
269 $total_cache / $GLOBALS['quota_cache'] > 1.5
270 and $n * 50 > $trop
271 ) {
272 $encore = true;
273 spip_log('Il faut encore purger', 'invalideur');
274 }
275 }
276 }
277
278 return $encore;
279 }
280
281
282 //
283 // Destruction des fichiers caches invalides
284 //
285
286 // Securite : est sur que c'est un cache
287 // http://code.spip.net/@retire_cache
288 function retire_cache($cache) {
289
290 if (preg_match(
291 ',^([0-9a-f]/)?([0-9]+/)?[0-9a-f]+\.cache(\.gz)?$,i',
292 $cache
293 )) {
294 // supprimer le fichier (de facon propre)
295 supprimer_fichier(_DIR_CACHE . $cache);
296 } else {
297 spip_log("Nom de fichier cache incorrect : $cache");
298 }
299 }
300
301 #######################################################################
302 ##
303 ## Ci-dessous les fonctions qui restent appellees dans le core
304 ## pour pouvoir brancher le plugin invalideur ;
305 ## mais ici elles ne font plus rien
306 ##
307
308 // Supprimer les caches marques "x"
309 // A priori dans cette version la fonction ne sera pas appelee, car
310 // la meta est toujours false ; mais evitons un bug si elle est appellee
311 // http://code.spip.net/@retire_caches
312 function retire_caches($chemin = '') {
313 if (isset($GLOBALS['meta']['invalider_caches'])) {
314 effacer_meta('invalider_caches');
315 } # concurrence
316 }
317
318
319 // Fonction permettant au compilo de calculer les invalideurs d'une page
320 // (note: si absente, n'est pas appellee)
321 /*
322 // http://code.spip.net/@calcul_invalideurs
323 function calcul_invalideurs($corps, $primary, &$boucles, $id_boucle) {
324 return $corps;
325 }
326 */
327
328 // Cette fonction permet de supprimer tous les invalideurs
329 // Elle ne touche pas aux fichiers cache eux memes ; elle est
330 // invoquee quand on vide tout le cache en bloc (action/purger)
331 //
332 // http://code.spip.net/@supprime_invalideurs
333 function supprime_invalideurs() {
334 }
335
336
337 // Calcul des pages : noter dans la base les liens d'invalidation
338 // http://code.spip.net/@maj_invalideurs
339 function maj_invalideurs($fichier, &$page) {
340 }
341
342
343 // les invalideurs sont de la forme "objet/id_objet"
344 // http://code.spip.net/@insere_invalideur
345 function insere_invalideur($inval, $fichier) {
346 }
347
348
349 //
350 // Marquer les fichiers caches invalides comme etant a supprimer
351 //
352 // http://code.spip.net/@applique_invalideur
353 function applique_invalideur($depart) {
354 }