|
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
|
|
|
/** |
|
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 inc_suivre_invalideur_dist($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 & CACHEDIR.TAG |
|
192
|
|
|
if ($fichier[0] == '.' or $fichier == 'CACHEDIR.TAG') { |
|
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
|
|
|
// Destruction des fichiers caches invalides |
|
228
|
|
|
// |
|
229
|
|
|
|
|
230
|
|
|
// Securite : est sur que c'est un cache |
|
231
|
|
|
// http://code.spip.net/@retire_cache |
|
232
|
|
|
function retire_cache($cache) { |
|
233
|
|
|
|
|
234
|
|
|
if (preg_match( |
|
235
|
|
|
',^([0-9a-f]/)?([0-9]+/)?[0-9a-f]+\.cache(\.gz)?$,i', |
|
236
|
|
|
$cache |
|
237
|
|
|
)) { |
|
238
|
|
|
// supprimer le fichier (de facon propre) |
|
239
|
|
|
supprimer_fichier(_DIR_CACHE . $cache); |
|
240
|
|
|
} else { |
|
241
|
|
|
spip_log("Nom de fichier cache incorrect : $cache"); |
|
242
|
|
|
} |
|
243
|
|
|
} |
|
244
|
|
|
|
|
245
|
|
|
// Supprimer les caches marques "x" |
|
246
|
|
|
// A priori dans cette version la fonction ne sera pas appelee, car |
|
247
|
|
|
// la meta est toujours false ; mais evitons un bug si elle est appellee |
|
248
|
|
|
// http://code.spip.net/@retire_caches |
|
249
|
|
|
function inc_retire_caches_dist($chemin = '') { |
|
|
|
|
|
|
250
|
|
|
if (isset($GLOBALS['meta']['invalider_caches'])) { |
|
251
|
|
|
effacer_meta('invalider_caches'); |
|
252
|
|
|
} # concurrence |
|
253
|
|
|
} |
|
254
|
|
|
|
|
255
|
|
|
####################################################################### |
|
256
|
|
|
## |
|
257
|
|
|
## Ci-dessous les fonctions qui restent appellees dans le core |
|
258
|
|
|
## pour pouvoir brancher le plugin invalideur ; |
|
259
|
|
|
## mais ici elles ne font plus rien |
|
260
|
|
|
## |
|
261
|
|
|
|
|
262
|
|
|
function retire_caches($chemin = '') { |
|
263
|
|
|
if ($retire_caches = charger_fonction('retire_caches', 'inc', true)) { |
|
264
|
|
|
return $retire_caches($chemin); |
|
265
|
|
|
} |
|
266
|
|
|
} |
|
267
|
|
|
|
|
268
|
|
|
|
|
269
|
|
|
// Fonction permettant au compilo de calculer les invalideurs d'une page |
|
270
|
|
|
// (note: si absente, n'est pas appellee) |
|
271
|
|
|
|
|
272
|
|
|
// http://code.spip.net/@calcul_invalideurs |
|
273
|
|
|
function calcul_invalideurs($corps, $primary, &$boucles, $id_boucle) { |
|
274
|
|
|
if ($calcul_invalideurs = charger_fonction('calcul_invalideurs', 'inc', true)) { |
|
275
|
|
|
return $calcul_invalideurs($corps, $primary, $boucles, $id_boucle); |
|
276
|
|
|
} |
|
277
|
|
|
return $corps; |
|
278
|
|
|
} |
|
279
|
|
|
|
|
280
|
|
|
|
|
281
|
|
|
// Cette fonction permet de supprimer tous les invalideurs |
|
282
|
|
|
// Elle ne touche pas aux fichiers cache eux memes ; elle est |
|
283
|
|
|
// invoquee quand on vide tout le cache en bloc (action/purger) |
|
284
|
|
|
// |
|
285
|
|
|
// http://code.spip.net/@supprime_invalideurs |
|
286
|
|
|
function supprime_invalideurs() { |
|
287
|
|
|
if ($supprime_invalideurs = charger_fonction('supprime_invalideurs', 'inc', true)) { |
|
288
|
|
|
return $supprime_invalideurs(); |
|
289
|
|
|
} |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
|
|
293
|
|
|
// Calcul des pages : noter dans la base les liens d'invalidation |
|
294
|
|
|
// http://code.spip.net/@maj_invalideurs |
|
295
|
|
|
function maj_invalideurs($fichier, &$page) { |
|
296
|
|
|
if ($maj_invalideurs = charger_fonction('maj_invalideurs', 'inc', true)) { |
|
297
|
|
|
return $maj_invalideurs($fichier, $page); |
|
298
|
|
|
} |
|
299
|
|
|
} |
|
300
|
|
|
|
|
301
|
|
|
|
|
302
|
|
|
// les invalideurs sont de la forme "objet/id_objet" |
|
303
|
|
|
// http://code.spip.net/@insere_invalideur |
|
304
|
|
|
function insere_invalideur($inval, $fichier) { |
|
305
|
|
|
if ($insere_invalideur = charger_fonction('insere_invalideur', 'inc', true)) { |
|
306
|
|
|
return $insere_invalideur($inval, $fichier); |
|
307
|
|
|
} |
|
308
|
|
|
} |
|
309
|
|
|
|
|
310
|
|
|
// |
|
311
|
|
|
// Marquer les fichiers caches invalides comme etant a supprimer |
|
312
|
|
|
// |
|
313
|
|
|
// http://code.spip.net/@applique_invalideur |
|
314
|
|
|
function applique_invalideur($depart) { |
|
315
|
|
|
if ($applique_invalideur = charger_fonction('applique_invalideur', 'inc', true)) { |
|
316
|
|
|
return $applique_invalideur($depart); |
|
317
|
|
|
} |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
// |
|
321
|
|
|
// Invalider les caches liés à telle condition |
|
322
|
|
|
// |
|
323
|
|
|
function suivre_invalideur($cond, $modif = true) { |
|
324
|
|
|
if ($suivre_invalideur = charger_fonction('suivre_invalideur', 'inc', true)) { |
|
325
|
|
|
return $suivre_invalideur($cond, $modif); |
|
326
|
|
|
} |
|
327
|
|
|
} |
This check looks for
@paramannotations where the type inferred by our type inference engine differs from the declared type.It makes a suggestion as to what type it considers more descriptive.
Most often this is a case of a parameter that can be null in addition to its declared types.