3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
6 * Copyright (c) 2001-2014 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
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 \***************************************************************************/
13 if (!defined('_ECRIRE_INC_VERSION')) return;
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
20 // http://doc.spip.org/@generer_nom_fichier_cache
21 function generer_nom_fichier_cache($contexte, $page) {
23 $cache = $page['contexte_implicite']['cache'] . '-';
25 foreach ($contexte as $var=>$val) {
26 $val = is_array($val) ?
var_export($val,true) : strval($val);
27 $cache .= str_replace('-', '_', $val) . '-' ;
30 $cache = str_replace('/', '_', rawurlencode($cache));
32 if (strlen($cache) > 24) {
33 $cache = preg_replace('/([a-zA-Z]{1,3})[^-_]*[-_]/', '\1-', $cache);
34 $cache = substr($cache, 0, 24);
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...)
43 var_export($page['contexte_implicite'],true) . ' '
44 . var_export($contexte,true)
47 $cache .= '-'.substr($md_cache, 1, 32-strlen($cache));
49 // Sous-repertoires 0...9a..f ; ne pas prendre la base _DIR_CACHE
52 $rep = sous_repertoire($rep, '', false,true);
53 $subdir = sous_repertoire($rep, substr($md_cache, 0, 1), true,true);
54 return $subdir.$cache;
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
60 // http://doc.spip.org/@gzip_page
61 function gzip_page($page) {
62 if (function_exists('gzcompress') AND strlen($page['texte']) > 16*1024) {
64 $page['texte'] = gzcompress($page['texte']);
71 // Faut-il decompresser ce cache ?
72 // (passage par reference pour alleger)
73 // http://doc.spip.org/@gunzip_page
74 function gunzip_page(&$page) {
76 $page['texte'] = gzuncompress($page['texte']);
77 $page['gz'] = false; // ne pas gzuncompress deux fois une meme page
82 * gestion des delais d'expiration du cache...
83 * $page passee par reference pour accelerer
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
94 /// http://doc.spip.org/@cache_valide
95 function cache_valide(&$page, $date) {
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;
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') {
105 // Cache invalide par la meta 'derniere_modif'
106 // sauf pour les bots, qui utilisent toujours le cache
108 AND $GLOBALS['derniere_modif_invalide']
109 AND $date < $GLOBALS['meta']['derniere_modif'])
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();
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}
129 else if ($date +
$duree < time())
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) {
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'))
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);
159 'invalideurs' => array('session' => ''),
160 'lastmodified' => time()
162 ecrire_fichier(_DIR_CACHE
. $chemin_cache, serialize($tmp));
164 $chemin_cache .= '_'.$page['invalideurs']['session'];
167 // ajouter la date de production dans le cache lui meme
168 // (qui contient deja sa duree de validite)
169 $page['lastmodified'] = time();
172 // l'enregistrer, compresse ou non...
173 $ok = ecrire_fichier(_DIR_CACHE
. $chemin_cache,
174 serialize(gzip_page($page)));
176 spip_log("Creation du cache $chemin_cache pour "
177 . $page['entetes']['X-Spip-Cache']." secondes". ($ok?
'':' (erreur!)'));
179 // Inserer ses invalideurs
180 include_spip('inc/invalideur');
181 maj_invalideurs($chemin_cache, $page);
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)
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
215 // http://doc.spip.org/@public_cacher_dist
216 function public_cacher_dist($contexte, &$use_cache, &$chemin_cache, &$page, &$lastmodified) {
218 // Second appel, destine a l'enregistrement du cache sur le disque
219 if (isset($chemin_cache)) return creer_cache($page, $chemin_cache);
221 // Toute la suite correspond au premier appel
222 $contexte_implicite = $page['contexte_implicite'];
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()) {
239 // Controler l'existence d'un cache nous correspondant
240 $chemin_cache = generer_nom_fichier_cache($contexte, $page);
243 // charger le cache s'il existe
244 if (lire_fichier(_DIR_CACHE
. $chemin_cache, $page))
245 $page = @unserialize
($page);
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;
261 // HEAD : cas sans jamais de calcul pour raisons de performance
262 if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
264 $page = array('contexte_implicite'=>$contexte_implicite);
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
))
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);
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
;
289 // determiner la validite de la 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;
296 // $page est un cache utilisable
301 $page = array('contexte_implicite'=>$contexte_implicite);
302 $use_cache = cache_valide($page,0); // fichier cache absent : provoque le calcul
305 // Si pas valide mais pas de connexion a la base, le garder quand meme
306 if (!spip_connect()) {
307 if (isset($page['texte'])) {
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'));
318 if ($use_cache < 0) $chemin_cache = '';