[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / ecrire / inc / invalideur.php
index 80536ba..fb9d0f5 100644 (file)
 /***************************************************************************\
  *  SPIP, Systeme de publication pour l'internet                           *
  *                                                                         *
- *  Copyright (c) 2001-2016                                                *
+ *  Copyright (c) 2001-2017                                                *
  *  Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James  *
  *                                                                         *
  *  Ce programme est un logiciel libre distribue sous licence GNU/GPL.     *
  *  Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne.   *
 \***************************************************************************/
 
+/**
+ * Gestion du cache et des invalidations de cache
+ *
+ * @package SPIP\Core\Cache
+ **/
 
-if (!defined('_ECRIRE_INC_VERSION')) return;
+if (!defined('_ECRIRE_INC_VERSION')) {
+       return;
+}
 
 include_spip('base/serial');
 
-# estime la taille moyenne d'un fichier cache, pour ne pas les regarder (10ko)
-define('_TAILLE_MOYENNE_FICHIER_CACHE', 1024 * 10);
-# si un fichier n'a pas servi (fileatime) depuis plus d'une heure, on se sent
-# en droit de l'eliminer
-define('_AGE_CACHE_ATIME', 3600);
+/** Estime la taille moyenne d'un fichier cache, pour ne pas les regarder (10ko) */
+if (!defined('_TAILLE_MOYENNE_FICHIER_CACHE')) {
+       define('_TAILLE_MOYENNE_FICHIER_CACHE', 1024 * 10);
+}
+/**
+ * Si un fichier n'a pas été servi (fileatime) depuis plus d'une heure, on se sent
+ * en droit de l'éliminer
+ */
+if (!defined('_AGE_CACHE_ATIME')) {
+       define('_AGE_CACHE_ATIME', 3600);
+}
 
-// Donne le nombre de fichiers dans un repertoire (plat, pour aller vite)
-// false si erreur
-// http://doc.spip.org/@nombre_de_fichiers_repertoire
-function nombre_de_fichiers_repertoire($dir,$nb_estim_taille = 20) {
+/**
+ * Calcul le nombre de fichiers à la racine d'un répertoire ainsi qu'une
+ * approximation de la taille du répertoire
+ *
+ * On ne calcule que la racine pour pour aller vite.
+ *
+ * @param string $dir Chemin du répertoire
+ * @param string $nb_estim_taille Nombre de fichiers maximum pour estimer la taille
+ * @return bool|array
+ *
+ *     - false si le répertoire ne peut pas être ouvert
+ *     - array(nombre de fichiers, approximation de la taille en octet) sinon
+ **/
+function nombre_de_fichiers_repertoire($dir, $nb_estim_taille = 20) {
        $taille = 0; // mesurer la taille de N fichiers au hasard dans le repertoire
        $nb = $nb_estim_taille;
-       if (!$h = @opendir($dir)) return false;
+       if (!$h = opendir($dir)) {
+               return false;
+       }
        $total = 0;
-       while (($fichier = @readdir($h)) !== false)
-               if ($fichier[0]!='.' AND !is_dir("$dir/$fichier")){
+       while (($fichier = @readdir($h)) !== false) {
+               if ($fichier[0] != '.' and !is_dir("$dir/$fichier")) {
                        $total++;
-                       if ($nb AND rand(1,10)==1){
+                       if ($nb and rand(1, 10) == 1) {
                                $taille += filesize("$dir/$fichier");
                                $nb--;
                        }
                }
+       }
        closedir($h);
-       return array($total,$taille?$taille/($nb_estim_taille-$nb):_TAILLE_MOYENNE_FICHIER_CACHE);
+
+       return array($total, $taille ? $taille / ($nb_estim_taille - $nb) : _TAILLE_MOYENNE_FICHIER_CACHE);
 }
 
-// Indique la taille du repertoire cache ; pour de gros volumes,
-// impossible d'ouvrir chaque fichier, on y va donc a l'estime
-// http://doc.spip.org/@taille_du_cache
+
+/**
+ * Évalue approximativement la taille du cache
+ *
+ * Pour de gros volumes, impossible d'ouvrir chaque fichier,
+ * on y va donc à l'estime !
+ *
+ * @return int Taille approximative en octets
+ **/
 function taille_du_cache() {
-       $total = 0;
-       $taille = 0;
-       for ($i=0;$i<16;$i++) {
-               $l = dechex($i);
-               $dir = sous_repertoire(_DIR_CACHE, $l);
-               list($n,$s) = nombre_de_fichiers_repertoire($dir);
-               $total += $n;
-               $taille += $s;
+       # check dirs until we reach > 500 files
+       $t = 0;
+       $n = 0;
+       $time = isset($GLOBALS['meta']['cache_mark']) ? $GLOBALS['meta']['cache_mark'] : 0;
+       for ($i=0; $i < 256; $i++) {
+               $dir = _DIR_CACHE.sprintf('%02s', dechex($i));
+               if (@is_dir($dir) and is_readable($dir) and $d = opendir($dir)) {
+                       while (($f = readdir($d)) !== false) {
+                               if (preg_match(',^[[0-9a-f]+\.cache$,S', $f) and $a = stat("$dir/$f")) {
+                                       $n++;
+                                       if ($a['mtime'] >= $time) {
+                                               if ($a['blocks'] > 0) {
+                                                       $t += 512*$a['blocks'];
+                                               } else {
+                                                       $t += $a['size'];
+                                               }
+                                       }
+                               }
+                       }
+               }
+               if ($n > 500) {
+                       return intval(256*$t/(1+$i));
+               }
        }
-       return $total * $taille / 16;
+       return $t;
 }
 
-// Invalider les caches lies a telle condition
-// les invalideurs sont de la forme 'objet/id_objet'
-// la condition est generalement "id='objet/id_objet'"
-// ici on se contente de noter la date de mise a jour dans les metas
-// http://doc.spip.org/@suivre_invalideur
-function suivre_invalideur($cond, $modif=true) {
-       if (!$modif)
+
+/**
+ * Invalider les caches liés à telle condition
+ *
+ * Les invalideurs sont de la forme 'objet/id_objet'.
+ * La condition est géneralement "id='objet/id_objet'".
+ *
+ * Ici on se contente de noter la date de mise à jour dans les metas,
+ * pour le type d'objet en question (non utilisé cependant) et pour
+ * tout le site (sur la meta `derniere_modif`)
+ *
+ * @global derniere_modif_invalide
+ *     Par défaut à `true`, la meta `derniere_modif` est systématiquement
+ *     calculée dès qu'un invalideur se présente. Cette globale peut
+ *     être mise à `false` (aucun changement sur `derniere_modif`) ou
+ *     sur une liste de type d'objets (changements uniquement lorsqu'une
+ *     modification d'un des objets se présente).
+ *
+ * @param string $cond
+ *     Condition d'invalidation
+ * @param bool $modif
+ *     Inutilisé
+ **/
+function suivre_invalideur($cond, $modif = true) {
+       if (!$modif) {
                return;
+       }
 
        // determiner l'objet modifie : forum, article, etc
-       if (preg_match(',["\']([a-z_]+)[/"\'],', $cond, $r))
+       if (preg_match(',["\']([a-z_]+)[/"\'],', $cond, $r)) {
                $objet = objet_type($r[1]);
+       }
 
        // stocker la date_modif_$objet (ne sert a rien pour le moment)
-       if (isset($objet))
-               ecrire_meta('derniere_modif_'.$objet, time());
+       if (isset($objet)) {
+               ecrire_meta('derniere_modif_' . $objet, time());
+       }
 
        // si $derniere_modif_invalide est un array('article', 'rubrique')
        // n'affecter la meta que si un de ces objets est modifie
        if (is_array($GLOBALS['derniere_modif_invalide'])) {
-               if (in_array($objet, $GLOBALS['derniere_modif_invalide']))
+               if (in_array($objet, $GLOBALS['derniere_modif_invalide'])) {
                        ecrire_meta('derniere_modif', time());
-       }
-       // sinon, cas standard, toujours affecter la meta
-       else
+               }
+       // sinon, cas standard, toujours affecter la meta
+       else {
                ecrire_meta('derniere_modif', time());
-
+       }
 }
 
 
-
 /**
  * Purge un répertoire de ses fichiers
  *
  * Utilisée entre autres pour vider le cache depuis l'espace privé
  *
  * @uses supprimer_fichier()
- * 
+ *
  * @param string $dir
  *     Chemin du répertoire à purger
  * @param array $options
@@ -107,36 +175,47 @@ function suivre_invalideur($cond, $modif=true) {
  *     - limit : nombre maximum de suppressions
  * @return int
  *     Nombre de fichiers supprimés
-**/
-function purger_repertoire($dir, $options=array()) {
-       $handle = @opendir($dir);
-       if (!$handle) return;
+ **/
+function purger_repertoire($dir, $options = array()) {
+       if (!is_dir($dir) or !is_readable($dir)) {
+               return;
+       }
+       $handle = opendir($dir);
+       if (!$handle) {
+               return;
+       }
 
        $total = 0;
 
        while (($fichier = @readdir($handle)) !== false) {
                // Eviter ".", "..", ".htaccess", ".svn" etc.
-               if ($fichier[0] == '.') continue;
+               if ($fichier[0] == '.') {
+                       continue;
+               }
                $chemin = "$dir/$fichier";
                if (is_file($chemin)) {
-                       if (  (!isset($options['atime']) OR (@fileatime($chemin) < $options['atime']))
-                               AND (!isset($options['mtime']) OR (@filemtime($chemin) < $options['mtime']))
-                         ) {
+                       if ((!isset($options['atime']) or (@fileatime($chemin) < $options['atime']))
+                               and (!isset($options['mtime']) or (@filemtime($chemin) < $options['mtime']))
+                       ) {
                                supprimer_fichier($chemin);
-                               $total ++;
+                               $total++;
+                       }
+               } else {
+                       if (is_dir($chemin)) {
+                               $opts = $options;
+                               if (isset($options['limit'])) {
+                                       $opts['limit'] = $options['limit'] - $total;
+                               }
+                               $total += purger_repertoire($chemin, $opts);
+                               if (isset($options['subdir']) && $options['subdir']) {
+                                       spip_unlink($chemin);
+                               }
                        }
-               }
-               else if (is_dir($chemin)){
-                       $opts = $options;
-                       if (isset($otpions['limit']))
-                               $otps['limit'] = $otpions['limit'] - $total;
-                       $total += purger_repertoire($chemin, $opts);
-                       if (isset($options['subdir']) && $options['subdir'])
-                               spip_unlink($chemin);
                }
 
-               if (isset($options['limit']) AND $total>=$options['limit'])
+               if (isset($options['limit']) and $total >= $options['limit']) {
                        break;
+               }
        }
        closedir($handle);
 
@@ -150,48 +229,52 @@ function purger_repertoire($dir, $options=array()) {
 // et on le ratiboise en supprimant les fichiers qui n'ont pas
 // ete sollicites dans l'heure qui vient de s'ecouler
 //
-// http://doc.spip.org/@appliquer_quota_cache
+// http://code.spip.net/@appliquer_quota_cache
 function appliquer_quota_cache() {
-       global $quota_cache;
+
        $encore = false;
 
-       $tour_quota_cache = intval(1+$GLOBALS['meta']['tour_quota_cache'])%16;
+       $tour_quota_cache = intval(1 + $GLOBALS['meta']['tour_quota_cache']) % 16;
        ecrire_meta('tour_quota_cache', $tour_quota_cache);
 
        $l = dechex($tour_quota_cache);
        $dir = sous_repertoire(_DIR_CACHE, $l);
-       list($nombre,$taille) = nombre_de_fichiers_repertoire($dir);
+       list($nombre, $taille) = nombre_de_fichiers_repertoire($dir);
        $total_cache = $taille * $nombre;
        spip_log("Taille du CACHE estimee ($l): "
-               .(intval(16*$total_cache/(1024*1024/10))/10)." Mo","invalideur");
+               . (intval(16 * $total_cache / (1024 * 1024 / 10)) / 10) . ' Mo', 'invalideur');
 
        // Nombre max de fichiers a supprimer
-       if ($quota_cache > 0
-       AND $taille > 0) {
-               $trop = $total_cache - ($quota_cache/16)*1024*1024;
+       if ($GLOBALS['quota_cache'] > 0
+               and $taille > 0
+       ) {
+               $trop = $total_cache - ($GLOBALS['quota_cache'] / 16) * 1024 * 1024;
                $trop = 3 * intval($trop / $taille);
                if ($trop > 0) {
-                       $n = purger_repertoire($dir,
+                       $n = purger_repertoire(
+                               $dir,
                                array(
                                        'atime' => time() - _AGE_CACHE_ATIME,
                                        'limit' => $trop,
                                        'subdir' => true // supprimer les vieux sous repertoire de session (avant [15851])
                                )
                        );
-                       spip_log("$dir : $n/$trop caches supprimes [taille moyenne $taille]","invalideur");
-                       $total_cache = intval(max(0,(16*$total_cache) - $n*$taille)/(1024*1024)*10)/10;
-                       spip_log("cache restant estime : $total_cache Mo, ratio ".$total_cache/$quota_cache,"invalideur");
+                       spip_log("$dir : $n/$trop caches supprimes [taille moyenne $taille]", 'invalideur');
+                       $total_cache = intval(max(0, (16 * $total_cache) - $n * $taille) / (1024 * 1024) * 10) / 10;
+                       spip_log("cache restant estime : $total_cache Mo, ratio " . $total_cache / $GLOBALS['quota_cache'], 'invalideur');
 
                        // redemander la main pour eviter que le cache ne gonfle trop
                        // mais pas si on ne peut pas purger car les fichiers sont trops recents
                        if (
-                         $total_cache/$quota_cache>1.5
-                         AND $n*50>$trop) {
+                               $total_cache / $GLOBALS['quota_cache'] > 1.5
+                               and $n * 50 > $trop
+                       ) {
                                $encore = true;
-                               spip_log("Il faut encore purger","invalideur");
+                               spip_log('Il faut encore purger', 'invalideur');
                        }
                }
        }
+
        return $encore;
 }
 
@@ -201,16 +284,18 @@ function appliquer_quota_cache() {
 //
 
 // Securite : est sur que c'est un cache
-// http://doc.spip.org/@retire_cache
+// http://code.spip.net/@retire_cache
 function retire_cache($cache) {
 
        if (preg_match(
-       "|^([0-9a-f]/)?([0-9]+/)?[^.][\-_\%0-9a-z]+--[0-9a-f]+(\.gz)?$|i",
-       $cache)) {
+               ',^([0-9a-f]/)?([0-9]+/)?[0-9a-f]+\.cache(\.gz)?$,i',
+               $cache
+       )) {
                // supprimer le fichier (de facon propre)
                supprimer_fichier(_DIR_CACHE . $cache);
-       } else
+       } else {
                spip_log("Nom de fichier cache incorrect : $cache");
+       }
 }
 
 #######################################################################
@@ -223,17 +308,18 @@ function retire_cache($cache) {
 // Supprimer les caches marques "x"
 // A priori dans cette version la fonction ne sera pas appelee, car
 // la meta est toujours false ; mais evitons un bug si elle est appellee
-// http://doc.spip.org/@retire_caches
+// http://code.spip.net/@retire_caches
 function retire_caches($chemin = '') {
-       if (isset($GLOBALS['meta']['invalider_caches']))
-               effacer_meta('invalider_caches'); # concurrence
+       if (isset($GLOBALS['meta']['invalider_caches'])) {
+               effacer_meta('invalider_caches');
+       } # concurrence
 }
 
 
 // Fonction permettant au compilo de calculer les invalideurs d'une page
 // (note: si absente, n'est pas appellee)
 /*
-// http://doc.spip.org/@calcul_invalideurs
+// http://code.spip.net/@calcul_invalideurs
 function calcul_invalideurs($corps, $primary, &$boucles, $id_boucle) {
        return $corps;
 }
@@ -243,23 +329,26 @@ function calcul_invalideurs($corps, $primary, &$boucles, $id_boucle) {
 // Elle ne touche pas aux fichiers cache eux memes ; elle est
 // invoquee quand on vide tout le cache en bloc (action/purger)
 //
-// http://doc.spip.org/@supprime_invalideurs
-function supprime_invalideurs() { }
+// http://code.spip.net/@supprime_invalideurs
+function supprime_invalideurs() {
+}
 
 
 // Calcul des pages : noter dans la base les liens d'invalidation
-// http://doc.spip.org/@maj_invalideurs
-function maj_invalideurs ($fichier, &$page) { }
+// http://code.spip.net/@maj_invalideurs
+function maj_invalideurs($fichier, &$page) {
+}
+
 
 // les invalideurs sont de la forme "objet/id_objet"
-// http://doc.spip.org/@insere_invalideur
-function insere_invalideur($inval, $fichier) { }
+// http://code.spip.net/@insere_invalideur
+function insere_invalideur($inval, $fichier) {
+}
 
 
 //
 // Marquer les fichiers caches invalides comme etant a supprimer
 //
-// http://doc.spip.org/@applique_invalideur
-function applique_invalideur($depart) { }
-
-?>
+// http://code.spip.net/@applique_invalideur
+function applique_invalideur($depart) {
+}