source: spip-zone/_plugins_/memoization/branches/v1/public/cacher.php @ 104337

Last change on this file since 104337 was 104337, checked in by spip.franck@…, 4 years ago
File size: 14.8 KB
Line 
1<?php
2
3/***************************************************************************\
4 *  SPIP, Systeme de publication pour l'internet                           *
5 *                                                                         *
6 *  Copyright (c) 2001-2010                                                *
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
13if (!defined("_ECRIRE_INC_VERSION")) return;
14
15// par defaut pas de progressivite d'invalidation du cache : duree=0
16if (!defined("_DUREE_INVALIDATION_PROGRESSIVE_CACHE"))
17        define("_DUREE_INVALIDATION_PROGRESSIVE_CACHE",0);
18
19/* compat SPIP 1.9 */
20if(!function_exists('test_espace_prive')) {
21        function test_espace_prive() {
22                return !!_DIR_RACINE;
23        }
24}
25/* compat SPIP 2.1 */
26if(defined('_LOG_INFO_IMPORTANTE')) {
27        function memoization_log($log) { spip_log($log, _LOG_INFO_IMPORTANTE); }
28} else {
29        function memoization_log($log) { spip_log($log); }
30}
31
32// https://code.spip.net/@generer_nom_fichier_cache
33function generer_nom_fichier_cache($contexte, $page) {
34        // l'indicateur sert a savoir un peu de quoi il s'agit
35        // quand on regarde dans le cache ; on le met a la fin
36        // du nom pour que ca "melange" mieux sous memcache
37        $indicateur = is_array($page)
38                ? $page['contexte_implicite']['cache'] # SPIP 2.1
39                : strval($page); # SPIP 2.0 ou autre
40
41        return
42                md5(var_export(array($contexte, $page),true).'-'.$GLOBALS['dossier_squelettes'].'-'.(isset($GLOBALS['marqueur'])?$GLOBALS['marqueur']:''))
43                . '-'.$indicateur;
44}
45
46// Parano : on signe le cache, afin d'interdire un hack d'injection
47// dans notre memcache
48function cache_signature(&$page) {
49        if (!isset($GLOBALS['meta']['cache_signature'])){
50                include_spip('inc/acces');
51                include_spip('auth/sha256.inc');
52                $sigfunc = function_exists('_nano_sha256') ? '_nano_sha256' : 'md5';
53                ecrire_meta('cache_signature', $sigfunc($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SERVER_SIGNATURE"] . creer_uniqid()), 'non');
54        }
55        return crc32($GLOBALS['meta']['cache_signature'].$page['texte']);
56}
57
58/**
59 * gestion des delais d'expiration du cache...
60 * $page passee par reference pour accelerer
61 *
62 * La fonction retourne
63 * 1 si il faut mettre le cache a jour
64 * 0 si le cache est valide
65 * -1 si il faut calculer sans stocker en cache
66 *
67 * @param array $page
68 * @param int $date
69 * @return int -1|0|1
70 */
71/// https://code.spip.net/@cache_valide
72function cache_valide(&$page, $date) {
73        $now = $_SERVER['REQUEST_TIME'];
74
75        // Apparition d'un nouvel article post-date ?
76        if ($GLOBALS['meta']['post_dates'] == 'non'
77          AND isset($GLOBALS['meta']['date_prochain_postdate'])
78          AND $now > $GLOBALS['meta']['date_prochain_postdate']) {
79                spip_log('Un article post-date invalide le cache');
80                include_spip('inc/rubriques');
81                calculer_prochain_postdate(true);
82        }
83
84        if (defined('_VAR_NOCACHE') AND _VAR_NOCACHE) return -1;
85        if (isset($GLOBALS['meta']['cache_inhib']) AND $_SERVER['REQUEST_TIME'] AND $_SERVER['REQUEST_TIME']<$GLOBALS['meta']['cache_inhib']) return -1;
86        if (isset($GLOBALS['var_nocache']) AND $GLOBALS['var_nocache']) return -1;
87        if (defined('_NO_CACHE')) return (_NO_CACHE==0 AND !isset($page['texte']))?1:_NO_CACHE;
88
89        // pas de cache ? on le met a jour, sauf pour les bots (on leur calcule la page sans mise en cache)
90        if (!$page OR !isset($page['texte']) OR !isset($page['entetes']['X-Spip-Cache'])) return _IS_BOT?-1:1;
91
92        // controle de la signature
93        if ($page['sig'] !== cache_signature($page))
94                return 1;
95
96        // #CACHE{n,statique} => on n'invalide pas avec derniere_modif
97        // cf. ecrire/public/balises.php, balise_CACHE_dist()
98        if (!isset($page['entetes']['X-Spip-Statique']) OR $page['entetes']['X-Spip-Statique'] !== 'oui') {
99
100                // Cache invalide par la meta 'derniere_modif'
101                // sauf pour les bots, qui utilisent toujours le cache
102                if (!_IS_BOT
103                  AND $GLOBALS['derniere_modif_invalide']
104                  AND $date < $GLOBALS['meta']['derniere_modif']){
105                        // pour les admins on invalide tout de suite pour leur permettre de voir les modifs
106                        if (isset($GLOBALS['visiteur_session']['statut'])
107                          AND $GLOBALS['visiteur_session']['statut']=='0minirezo')
108                                return 1;
109
110                        // pour les autres on invalide progressivement pour repartir la charge
111                        // avec une fonction de probabilite lineaire qui vaut
112                        // 5% quand t=derniere_modif
113                        // 100% quand t=derniere_modif+_DUREE_INVALIDATION_PROGRESSIVE_CACHE
114                        static $refresh_ok = null;
115                        if (is_null($refresh_ok)){
116                                $dt = $_SERVER['REQUEST_TIME']-$GLOBALS['meta']['derniere_modif'];
117                                if ($dt>=_DUREE_INVALIDATION_PROGRESSIVE_CACHE){
118                                        $refresh_ok = 1;
119                                        #spip_log("Cache refresh systematique : REFRESH", "dbgcache");
120                                }
121                                else {
122                                        $coeff = (1-$dt/_DUREE_INVALIDATION_PROGRESSIVE_CACHE);
123                                        $seuil = 15; // 15% de probabilite au depart
124                                        $prob = mt_rand(1, $seuil+(100-$seuil)*$coeff);
125                                        $refresh_ok = ($prob<$seuil ? 1 : 0);
126                                        #spip_log("Cache refresh progresif dt=$dt coeff=$coeff p=$prob" . ($refresh_ok ? " : REFRESH" : ""), "dbgcache");
127                                }
128                        }
129
130                        // si pas de refresh force on laisse la main a la comparaison de date
131                        // selon duree de cache
132                        if ($refresh_ok)
133                                return 1;
134                }
135
136        }
137
138        // Sinon comparer l'age du fichier a sa duree de cache
139        $duree = intval($page['entetes']['X-Spip-Cache']);
140        $cache_mark = (isset($GLOBALS['meta']['cache_mark'])?$GLOBALS['meta']['cache_mark']:0);
141        if ($duree == 0)  #CACHE{0}
142                return -1;
143        // sauf pour les bots, qui utilisent toujours le cache
144        else if ((!_IS_BOT AND $date + $duree < $now)
145                # le cache est anterieur a la derniere purge : l'ignorer, meme pour les bots
146          OR $date<$cache_mark) {
147                if (_IS_BOT) return -1;
148               
149                // si la charge est trop elevee on accepte de prendre un vieux cache
150                $load = function_exists('sys_getloadavg') ? sys_getloadavg() : array(0);
151                if ($load[0]>20) {
152                        spip_log('load eleve ('. intval($load[0]).'), utilisation du cache pour '.var_export($page['source'],true).' sur '.self(), 'debug');
153                        return 0;
154                }
155
156                // sinon on calcule
157                return 1;
158        } else {
159                return 0;
160        }
161}
162
163// Creer le fichier cache
164# Passage par reference de $page par souci d'economie
165// https://code.spip.net/@creer_cache
166function creer_cache(&$page, &$chemin_cache, &$memo) {
167
168        // Ne rien faire si on est en preview, debug, ou si une erreur
169        // grave s'est presentee (compilation du squelette, MySQL, etc)
170        // le cas var_nocache ne devrait jamais arriver ici (securite)
171        // le cas spip_interdire_cache correspond a une ereur SQL grave non anticipable
172        if ((defined('_VAR_NOCACHE') AND _VAR_NOCACHE)
173                OR (isset($GLOBALS['var_nocache']) AND $GLOBALS['var_nocache']) // compat SPIP 2.x
174                OR defined('spip_interdire_cache'))
175                return;
176
177        // Si la page c1234 a un invalideur de session 'zz', sauver dans
178        // 'tmp/cache/MD5(chemin_cache)_zz'
179        if (isset($page['invalideurs'])
180        AND isset($page['invalideurs']['session'])) {
181                // on verifie que le contenu du chemin cache indique seulement
182                // "cache sessionne" ; sa date indique la date de validite
183                // des caches sessionnes
184                if (!is_array($tmp = $memo->get($chemin_cache))) {
185                        $tmp = array(
186                                'invalideurs' => array('session' => ''),
187                                'lastmodified' => $_SERVER['REQUEST_TIME']
188                        );
189                        $ok = $memo->set($chemin_cache, $tmp);
190                        spip_log((_IS_BOT?"Bot:":"")."Creation du cache sessionne $chemin_cache ". $memo->methode ." pour "
191                                . $page['entetes']['X-Spip-Cache']." secondes". ($ok?'':' (erreur!)'));
192                }
193                $chemin_cache .= '_'.$page['invalideurs']['session'];
194        }
195
196        // ajouter la date de production dans le cache lui meme
197        // (qui contient deja sa duree de validite)
198        $page['lastmodified'] = $_SERVER['REQUEST_TIME'];
199
200        // signer le contenu
201        $page['sig']= cache_signature($page);
202
203        // compresser si elle est > 16 ko
204        if (strlen($page['texte']) > 16384
205        AND function_exists('gzcompress')) {
206                $page['texte'] = gzcompress($z = $page['texte']);
207                $page['gz'] = true;
208        }
209
210        // memoizer...
211        // on ajoute une heure histoire de pouvoir tourner
212        // sur le cache quand la base de donnees est plantee (a tester)
213        $ok = $memo->set($chemin_cache, $page, 3600+$page['entetes']['X-Spip-Cache']);
214
215        // retablir le texte pour l'afficher
216        if (isset($z)) {
217                $page['texte'] = $z;
218                unset($page['gz']);
219        }
220
221        spip_log((_IS_BOT?"Bot:":"")."Creation du cache $chemin_cache ". $memo->methode ." pour "
222                . $page['entetes']['X-Spip-Cache']." secondes". ($ok?'':' (erreur!)'));
223
224        // Inserer ses invalideurs
225        /* compat SPIP 1.9 : ne pas appeler les invalideurs du tout */
226        if (!(isset($GLOBALS['spip_version']) AND $GLOBALS['spip_version']<2)) {
227                include_spip('inc/invalideur');
228                maj_invalideurs($chemin_cache, $page);
229        }
230}
231
232
233// purger un petit cache (tidy ou recherche) qui ne doit pas contenir de
234// vieux fichiers ; (cette fonction ne sert que dans des plugins obsoletes)
235// https://code.spip.net/@nettoyer_petit_cache
236function nettoyer_petit_cache($prefix, $duree = 300) {
237        // determiner le repertoire a purger : 'tmp/CACHE/rech/'
238        $dircache = sous_repertoire(_DIR_CACHE,$prefix);
239        if (spip_touch($dircache.'purger_'.$prefix, $duree, true)) {
240                foreach (preg_files($dircache,'[.]txt$') as $f) {
241                        if ($_SERVER['REQUEST_TIME'] - (@file_exists($f)?@filemtime($f):0) > $duree)
242                                spip_unlink($f);
243                }
244        }
245}
246
247
248// Interface du gestionnaire de cache
249// Si son 3e argument est non vide, elle passe la main a creer_cache
250// Sinon, elle recoit un contexte (ou le construit a partir de REQUEST_URI)
251// et affecte les 4 autres parametres recus par reference:
252// - use_cache qui vaut
253//     -1 s'il faut calculer la page sans la mettre en cache
254//      0 si on peut utiliser un cache existant
255//      1 s'il faut calculer la page et la mettre en cache
256// - chemin_cache qui est le chemin d'acces au fichier ou vide si pas cachable
257// - page qui est le tableau decrivant la page, si le cache la contenait
258// - lastmodified qui vaut la date de derniere modif du fichier.
259// Elle retourne '' si tout va bien
260// un message d'erreur si le calcul de la page est totalement impossible
261
262// https://code.spip.net/@public_cacher_dist
263function public_cacher($contexte, &$use_cache, &$chemin_cache, &$page, &$lastmodified) {
264        $chemin_cache_session = false;
265       
266        /* compat SPIP 1.9 */
267        if (is_null($contexte) AND function_exists('nettoyer_uri'))
268                $contexte = array('uri' => nettoyer_uri());
269
270        static $memo;
271        if (!isset($memo)) {
272                // cas d'un appel depuis un fichier options charge avant celui de memoization
273                if (!class_exists("MCache")){
274                        include_spip("memoization_options");
275                }
276                $cfg = @unserialize($GLOBALS['meta']['memoization']);
277                $memo = new MCache((isset($cfg['pages']) AND $cfg['pages'])? $cfg['pages'] : $cfg['methode']);
278        }
279
280        /* compat SPIP 1.9 */
281        if (is_array($page) AND !isset($page['entetes']['X-Spip-Cache']))
282                $page['duree'] = $page['entetes']['X-Spip-Cache'] = isset($GLOBALS['delais']) ? $GLOBALS['delais'] : null;
283
284        // Second appel, destine a l'enregistrement du cache sur le disque
285        if (isset($chemin_cache)) return creer_cache($page, $chemin_cache, $memo);
286
287        // Toute la suite correspond au premier appel
288        $contexte_implicite = $page['contexte_implicite'];
289
290        // Cas ignorant le cache car completement dynamique
291        if ($_SERVER['REQUEST_METHOD'] == 'POST'
292        OR (substr($contexte_implicite['cache'],0,8)=='modeles/') 
293        OR (_request('connect'))
294// Mode auteur authentifie appelant de ecrire/ : il ne faut rien lire du cache
295// et n'y ecrire que la compilation des squelettes (pas les pages produites)
296// car les references aux repertoires ne sont pas relatifs a l'espace public
297        OR test_espace_prive()) {
298                $use_cache = -1;
299                $lastmodified = 0;
300                $chemin_cache = "";
301                $page = array();
302                return;
303        }
304
305        // Controler l'existence d'un cache nous correspondant
306        $chemin_cache = generer_nom_fichier_cache($contexte, $page);
307        $lastmodified = 0;
308
309        // charger le cache s'il existe
310        if (!is_array($page = $memo->get($chemin_cache)))
311                $page = array();
312
313        // s'il est sessionne, charger celui correspondant a notre session
314        if (isset($page['invalideurs'])
315        AND isset($page['invalideurs']['session'])) {
316                $chemin_cache_session = $chemin_cache . '_' . spip_session();
317                if (is_array($page_session = $memo->get($chemin_cache_session))
318                AND $page_session['lastmodified'] >= $page['lastmodified'])
319                        $page = $page_session;
320                else
321                        $page = array();
322        }
323
324        // dezip si on l'a zipe
325        if (isset($page['gz'])) {
326                $page['texte'] = gzuncompress($page['texte']);
327                unset($page['gz']);
328        }
329
330        if (intval($GLOBALS['spip_version_branche'])<3){
331                // HEAD : cas sans jamais de calcul pour raisons de performance
332                // supprime en SPIP 3 par https://core.spip.net/projects/spip/repository/revisions/19959
333                if ($_SERVER['REQUEST_METHOD'] == 'HEAD') {
334                        $use_cache = 0;
335                        $page = array('contexte_implicite'=>$contexte_implicite);
336                        return;
337                }
338        }
339
340        // Si un calcul, recalcul [ou preview, mais c'est recalcul] est demande,
341        // on supprime le cache
342        if (((isset($GLOBALS['var_mode']) && $GLOBALS['var_mode']) OR (defined('_VAR_MODE') && _VAR_MODE)) &&
343                (isset($_COOKIE['spip_session'])
344                || isset($_COOKIE['spip_admin'])
345                || @file_exists(_ACCESS_FILE_NAME))
346        ) {
347                $page = array('contexte_implicite'=>$contexte_implicite); // ignorer le cache deja lu
348                include_spip('inc/invalideur');
349                if (function_exists('retire_caches')) retire_caches($chemin_cache); # API invalideur inutile
350                $memo->del($chemin_cache);
351                if ($chemin_cache_session)
352                        $memo->del($chemin_cache_session);
353        }
354
355        // $delais par defaut (pour toutes les pages sans #CACHE{})
356        if (!isset($GLOBALS['delais'])) {
357                if (!defined('_DUREE_CACHE_DEFAUT')) {
358                        define('_DUREE_CACHE_DEFAUT', 24*3600);
359                }
360                $GLOBALS['delais'] = _DUREE_CACHE_DEFAUT;
361        }
362
363        // determiner la validite de la page
364        if ($page) {
365                $use_cache = cache_valide($page, isset($page['lastmodified']) ? $page['lastmodified']:null);
366                // le contexte implicite n'est pas stocke dans le cache, mais il y a equivalence
367                // par le nom du cache. On le reinjecte donc ici pour utilisation eventuelle au calcul
368                $page['contexte_implicite'] = $contexte_implicite;
369                if (!$use_cache)
370                        return;
371        } else {
372                $page = array('contexte_implicite'=>$contexte_implicite);
373                $use_cache = cache_valide($page,0); // fichier cache absent : provoque le calcul
374        }
375
376        // Si pas valide mais pas de connexion a la base, le garder quand meme
377        if (!spip_connect()) {
378                if (isset($page['texte'])) {
379                        $use_cache = 0;
380                }
381                else {
382                        include_spip('inc/config');
383                        spip_log("Erreur base de donnees, impossible utiliser " . lire_config('memoization/methode') . " $chemin_cache");
384                        include_spip('inc/minipres');
385                        return minipres(_T('info_travaux_titre'),  _T('titre_probleme_technique'));
386                }
387        }
388
389        if ($use_cache < 0) $chemin_cache = '';
390        return;
391}
392
393// Faut-il decompresser ce cache ?
394// (passage par reference pour alleger)
395// https://code.spip.net/@gunzip_page
396function gunzip_page(&$page) {
397        if ($page['gz']) {
398                $page['texte'] = gzuncompress($page['texte']);
399                $page['gz'] = false; // ne pas gzuncompress deux fois une meme page
400        }
401}
402
403?>
Note: See TracBrowser for help on using the repository browser.