[SPIP] ~2.1.12 -->2.1.25
[velocampus/web/www.git] / www / ecrire / public / cacher.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2014 *
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')) return;
14
15 //
16 // Le format souhaite : "a/bout-d-url.md5" (.gz s'ajoutera pour les gros caches)
17 // Attention a modifier simultanement le sanity check de
18 // la fonction retire_cache() de inc/invalideur
19 //
20 // http://doc.spip.org/@generer_nom_fichier_cache
21 function generer_nom_fichier_cache($contexte, $page) {
22
23 $cache = $page['contexte_implicite']['cache'] . '-';
24
25 foreach ($contexte as $var=>$val) {
26 $val = is_array($val) ? var_export($val,true) : strval($val);
27 $cache .= str_replace('-', '_', $val) . '-' ;
28 }
29
30 $cache = str_replace('/', '_', rawurlencode($cache));
31
32 if (strlen($cache) > 24) {
33 $cache = preg_replace('/([a-zA-Z]{1,3})[^-_]*[-_]/', '\1-', $cache);
34 $cache = substr($cache, 0, 24);
35 }
36
37 // Morceau de md5 selon HOST, $dossier_squelettes et $marqueur
38 // permet de changer le nom du cache en changeant ceux-ci
39 // donc, par exemple, de gerer differents dossiers de squelettes
40 // en parallele, ou de la "personnalisation" via un marqueur (dont la
41 // composition est totalement libre...)
42 $md_cache = md5(
43 var_export($page['contexte_implicite'],true) . ' '
44 . var_export($contexte,true)
45 );
46
47 $cache .= '-'.substr($md_cache, 1, 32-strlen($cache));
48
49 // Sous-repertoires 0...9a..f ; ne pas prendre la base _DIR_CACHE
50 $rep = _DIR_CACHE;
51
52 $rep = sous_repertoire($rep, '', false,true);
53 $subdir = sous_repertoire($rep, substr($md_cache, 0, 1), true,true);
54 return $subdir.$cache;
55 }
56
57 // Faut-il compresser ce cache ? A partir de 16ko ca vaut le coup
58 // (pas de passage par reference car on veut conserver la version non compressee
59 // pour l'afficher)
60 // http://doc.spip.org/@gzip_page
61 function gzip_page($page) {
62 if (function_exists('gzcompress') AND strlen($page['texte']) > 16*1024) {
63 $page['gz'] = true;
64 $page['texte'] = gzcompress($page['texte']);
65 } else {
66 $page['gz'] = false;
67 }
68 return $page;
69 }
70
71 // Faut-il decompresser ce cache ?
72 // (passage par reference pour alleger)
73 // http://doc.spip.org/@gunzip_page
74 function gunzip_page(&$page) {
75 if ($page['gz']) {
76 $page['texte'] = gzuncompress($page['texte']);
77 $page['gz'] = false; // ne pas gzuncompress deux fois une meme page
78 }
79 }
80
81 /**
82 * gestion des delais d'expiration du cache...
83 * $page passee par reference pour accelerer
84 *
85 * La fonction retourne
86 * 1 si il faut mettre le cache a jour
87 * 0 si le cache est valide
88 * -1 si il faut calculer sans stocker en cache
89 *
90 * @param array $page
91 * @param int $date
92 * @return -1/0/1
93 */
94 /// http://doc.spip.org/@cache_valide
95 function cache_valide(&$page, $date) {
96
97 if (isset($GLOBALS['var_nocache']) AND $GLOBALS['var_nocache']) return -1;
98 if (defined('_NO_CACHE')) return (_NO_CACHE==0 AND !isset($page['texte']))?1:_NO_CACHE;
99 if (!$page OR !isset($page['texte']) OR !isset($page['entetes']['X-Spip-Cache'])) return 1;
100
101 // #CACHE{n,statique} => on n'invalide pas avec derniere_modif
102 // cf. ecrire/public/balises.php, balise_CACHE_dist()
103 if (!isset($page['entetes']['X-Spip-Statique']) OR $page['entetes']['X-Spip-Statique'] !== 'oui') {
104
105 // Cache invalide par la meta 'derniere_modif'
106 // sauf pour les bots, qui utilisent toujours le cache
107 if (!_IS_BOT
108 AND $GLOBALS['derniere_modif_invalide']
109 AND $date < $GLOBALS['meta']['derniere_modif'])
110 return 1;
111
112 // Apparition d'un nouvel article post-date ?
113 if ($GLOBALS['meta']['post_dates'] == 'non'
114 AND isset($GLOBALS['meta']['date_prochain_postdate'])
115 AND time() > $GLOBALS['meta']['date_prochain_postdate']) {
116 spip_log('Un article post-date invalide le cache');
117 include_spip('inc/rubriques');
118 ecrire_meta('derniere_modif', time());
119 calculer_prochain_postdate();
120 return 1;
121 }
122
123 }
124
125 // Sinon comparer l'age du fichier a sa duree de cache
126 $duree = intval($page['entetes']['X-Spip-Cache']);
127 if ($duree == 0) #CACHE{0}
128 return -1;
129 else if ($date + $duree < time())
130 return 1;
131 else
132 return 0;
133 }
134
135 // Creer le fichier cache
136 # Passage par reference de $page par souci d'economie
137 // http://doc.spip.org/@creer_cache
138 function creer_cache(&$page, &$chemin_cache) {
139
140 // Ne rien faire si on est en preview, debug, ou si une erreur
141 // grave s'est presentee (compilation du squelette, MySQL, etc)
142 // le cas var_nocache ne devrait jamais arriver ici (securite)
143 // le cas spip_interdire_cache correspond a une ereur SQL grave non anticipable
144 if ((isset($GLOBALS['var_nocache']) AND $GLOBALS['var_nocache'])
145 OR defined('spip_interdire_cache'))
146 return;
147
148 // Si la page c1234 a un invalideur de session 'zz', sauver dans
149 // 'tmp/cache/MD5(chemin_cache)_zz'
150 if (isset($page['invalideurs'])
151 AND isset($page['invalideurs']['session'])) {
152 // on verifie que le contenu du chemin cache indique seulement
153 // "cache sessionne" ; sa date indique la date de validite
154 // des caches sessionnes
155 if (!lire_fichier(_DIR_CACHE . $chemin_cache, $tmp)
156 OR !$tmp = @unserialize($tmp)) {
157 spip_log('Creation cache sessionne '.$chemin_cache);
158 $tmp = array(
159 'invalideurs' => array('session' => ''),
160 'lastmodified' => time()
161 );
162 ecrire_fichier(_DIR_CACHE . $chemin_cache, serialize($tmp));
163 }
164 $chemin_cache .= '_'.$page['invalideurs']['session'];
165 }
166
167 // ajouter la date de production dans le cache lui meme
168 // (qui contient deja sa duree de validite)
169 $page['lastmodified'] = time();
170
171
172 // l'enregistrer, compresse ou non...
173 $ok = ecrire_fichier(_DIR_CACHE . $chemin_cache,
174 serialize(gzip_page($page)));
175
176 spip_log("Creation du cache $chemin_cache pour "
177 . $page['entetes']['X-Spip-Cache']." secondes". ($ok?'':' (erreur!)'));
178
179 // Inserer ses invalideurs
180 include_spip('inc/invalideur');
181 maj_invalideurs($chemin_cache, $page);
182
183 }
184
185
186 // purger un petit cache (tidy ou recherche) qui ne doit pas contenir de
187 // vieux fichiers ; (cette fonction ne sert que dans des plugins obsoletes)
188 // http://doc.spip.org/@nettoyer_petit_cache
189 function nettoyer_petit_cache($prefix, $duree = 300) {
190 // determiner le repertoire a purger : 'tmp/CACHE/rech/'
191 $dircache = sous_repertoire(_DIR_CACHE,$prefix);
192 if (spip_touch($dircache.'purger_'.$prefix, $duree, true)) {
193 foreach (preg_files($dircache,'[.]txt$') as $f) {
194 if (time() - (@file_exists($f)?@filemtime($f):0) > $duree)
195 spip_unlink($f);
196 }
197 }
198 }
199
200
201 // Interface du gestionnaire de cache
202 // Si son 3e argument est non vide, elle passe la main a creer_cache
203 // Sinon, elle recoit un contexte (ou le construit a partir de REQUEST_URI)
204 // et affecte les 4 autres parametres recus par reference:
205 // - use_cache qui vaut
206 // -1 s'il faut calculer la page sans la mettre en cache
207 // 0 si on peut utiliser un cache existant
208 // 1 s'il faut calculer la page et la mettre en cache
209 // - chemin_cache qui est le chemin d'acces au fichier ou vide si pas cachable
210 // - page qui est le tableau decrivant la page, si le cache la contenait
211 // - lastmodified qui vaut la date de derniere modif du fichier.
212 // Elle retourne '' si tout va bien
213 // un message d'erreur si le calcul de la page est totalement impossible
214
215 // http://doc.spip.org/@public_cacher_dist
216 function public_cacher_dist($contexte, &$use_cache, &$chemin_cache, &$page, &$lastmodified) {
217
218 // Second appel, destine a l'enregistrement du cache sur le disque
219 if (isset($chemin_cache)) return creer_cache($page, $chemin_cache);
220
221 // Toute la suite correspond au premier appel
222 $contexte_implicite = $page['contexte_implicite'];
223
224 // Cas ignorant le cache car completement dynamique
225 if ($_SERVER['REQUEST_METHOD'] == 'POST'
226 OR (substr($contexte_implicite['cache'],0,8)=='modeles/')
227 OR (_request('connect'))
228 // Mode auteur authentifie appelant de ecrire/ : il ne faut rien lire du cache
229 // et n'y ecrire que la compilation des squelettes (pas les pages produites)
230 // car les references aux repertoires ne sont pas relatifs a l'espace public
231 OR test_espace_prive()) {
232 $use_cache = -1;
233 $lastmodified = 0;
234 $chemin_cache = "";
235 $page = array();
236 return;
237 }
238
239 // Controler l'existence d'un cache nous correspondant
240 $chemin_cache = generer_nom_fichier_cache($contexte, $page);
241 $lastmodified = 0;
242
243 // charger le cache s'il existe
244 if (lire_fichier(_DIR_CACHE . $chemin_cache, $page))
245 $page = @unserialize($page);
246 else
247 $page = array();
248
249 // s'il est sessionne, charger celui correspondant a notre session
250 if (isset($page['invalideurs'])
251 AND isset($page['invalideurs']['session'])) {
252 $chemin_cache_session = $chemin_cache . '_' . spip_session();
253 if (lire_fichier(_DIR_CACHE . $chemin_cache_session, $page_session)
254 AND $page_session = @unserialize($page_session)
255 AND $page_session['lastmodified'] >= $page['lastmodified'])
256 $page = $page_session;
257 else
258 $page = array();
259 }
260
261 // HEAD : cas sans jamais de calcul pour raisons de performance
262 if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
263 $use_cache = 0;
264 $page = array('contexte_implicite'=>$contexte_implicite);
265 return;
266 }
267
268 // Si un calcul, recalcul [ou preview, mais c'est recalcul] est demande,
269 // on supprime le cache
270 if ($GLOBALS['var_mode'] &&
271 (isset($_COOKIE['spip_session'])
272 || isset($_COOKIE['spip_admin'])
273 || @file_exists(_ACCESS_FILE_NAME))
274 ) {
275 $page = array('contexte_implicite'=>$contexte_implicite); // ignorer le cache deja lu
276 include_spip('inc/invalideur');
277 retire_caches($chemin_cache); # API invalideur inutile
278 supprimer_fichier(_DIR_CACHE.$chemin_cache);
279 if ($chemin_cache_session)
280 supprimer_fichier(_DIR_CACHE.$chemin_cache_session);
281 }
282
283 // $delais par defaut (pour toutes les pages sans #CACHE{})
284 if (!isset($GLOBALS['delais'])) {
285 define('_DUREE_CACHE_DEFAUT', 24*3600);
286 $GLOBALS['delais'] = _DUREE_CACHE_DEFAUT;
287 }
288
289 // determiner la validite de la page
290 if ($page) {
291 $use_cache = cache_valide($page, $page['lastmodified']);
292 // le contexte implicite n'est pas stocke dans le cache, mais il y a equivalence
293 // par le nom du cache. On le reinjecte donc ici pour utilisation eventuelle au calcul
294 $page['contexte_implicite'] = $contexte_implicite;
295 if (!$use_cache) {
296 // $page est un cache utilisable
297 gunzip_page($page);
298 return;
299 }
300 } else {
301 $page = array('contexte_implicite'=>$contexte_implicite);
302 $use_cache = cache_valide($page,0); // fichier cache absent : provoque le calcul
303 }
304
305 // Si pas valide mais pas de connexion a la base, le garder quand meme
306 if (!spip_connect()) {
307 if (isset($page['texte'])) {
308 gunzip_page($page);
309 $use_cache = 0;
310 }
311 else {
312 spip_log("Erreur base de donnees, impossible utiliser $chemin_cache");
313 include_spip('inc/minipres');
314 return minipres(_T('info_travaux_titre'), _T('titre_probleme_technique'));
315 }
316 }
317
318 if ($use_cache < 0) $chemin_cache = '';
319 return;
320 }
321
322 ?>