source: spip-zone/_plugins_/memoization/trunk/public/cacher.php @ 113365

Last change on this file since 113365 was 113365, checked in by jluc@…, 8 days ago

plus besoin des résidus d'ancienne config maintenant qu'ils sont migrés

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