From 32fd6c2c3f78d3c7363a340794f6fb78bef1ce00 Mon Sep 17 00:00:00 2001
From: Fil <fil@rezo.net>
Date: Wed, 16 Nov 2005 16:11:30 +0000
Subject: [PATCH] Nouveau modele de gestion des statistiques, qui devrait etre
 plus robuste et dont le code est plus simple ; mais attention il stocke des
 tas de fichiers temporaires dans ecrire/data/

Quelques corrections dans le parcours des repertoires (un fichier "0" pouvait bloquer SPIP)
---
 ecrire/inc_cron.php3                  |  51 ++---
 ecrire/inc_documents.php3             |   2 +-
 ecrire/inc_flock.php3                 |  11 +-
 ecrire/inc_getdocument.php3           |   4 +-
 ecrire/inc_lang.php3                  |   2 +-
 ecrire/inc_lang_raccourcis.php3       |   4 +-
 ecrire/inc_logos.php3                 |   2 +-
 ecrire/inc_popularites.php3           |  81 --------
 ecrire/inc_session.php3               |   2 +-
 ecrire/inc_statistiques.php3          | 117 -----------
 ecrire/inc_visites.php3               | 281 +++++++++++++++++++++-----
 formulaires/inc-formulaire_admin.php3 |  20 ++
 inc-cache.php3                        |   6 +-
 inc-stats.php3                        |  72 +++----
 14 files changed, 317 insertions(+), 338 deletions(-)
 delete mode 100644 ecrire/inc_popularites.php3

diff --git a/ecrire/inc_cron.php3 b/ecrire/inc_cron.php3
index 3121e0b760..fcb3df4af1 100644
--- a/ecrire/inc_cron.php3
+++ b/ecrire/inc_cron.php3
@@ -159,12 +159,9 @@ function taches_generales() {
 	AND (lire_meta('quoi_de_neuf') == 'oui') AND _DIR_RESTREINT)
 		$taches_generales['mail']= 3600 * 24 * lire_meta('jours_neuf');
 
-	// Stat. Attention: la popularite DOIT preceder les visites
-	if (lire_meta("activer_statistiques") == "oui") {
-		$taches_generales['statistiques'] = 3600;
-		$taches_generales['popularites'] = 1800;
-		$taches_generales['visites'] = 3600 * 24;
-	}
+	// stats : toutes les 5 minutes on peut vider un panier de visites
+	if (lire_meta("activer_statistiques") == "oui")
+		$taches_generales['visites'] = 300; 
 
 	// syndication
 	if (lire_meta("activer_syndic") == "oui") 
@@ -215,43 +212,25 @@ function cron_sites($t) {
 	return $r;
 }
 
-// calcule les stats en plusieurs etapes par tranche de 100
-
-function cron_statistiques($t) {
-	$ref = calculer_n_referers(100);
+//
+// Calcule les stats en plusieurs etapes
+//
+function cron_visites($t) {
+	$encore = calculer_visites($t);
 
 	// Si ce n'est pas fini on redonne la meme date au fichier .lock
 	// pour etre prioritaire lors du cron suivant
-	if ($ref == 100) return (0 - $t);
-
-	// Supprimer les referers trop vieux
-	supprimer_referers();
-	supprimer_referers("article");
-	return 1;
-}
-
-function cron_popularites($t) {
-	calculer_popularites();
-	return 1;
-}
-
-function cron_visites($t) {
-	// Si le fichier .lock est absent, ne pas calculer (mais reparer la date
-	// du .lock de maniere a commencer a 00:00:01 demain).
-	if ($t) {
-		// il faut d'abord faire le calcul des popularites
-		include_ecrire('inc_popularites.php3');
+	if ($encore)
+		return (0 - $t);
+	else {
 		calculer_popularites();
-
-		calculer_visites();
+		return 1;
 	}
-
-	// il vaut mieux le lancer peu apres minuit, 
-	// donc on pretend avoir ete execute precisement "ce matin a 00:00:01"
-	// pour etre appele demain a la meme heure
-	return 0 - (strtotime(date("d F Y", time()))+60);
 }
 
+//
+// Mail des nouveautes
+//
 function cron_mail($t) {
 	$adresse_neuf = lire_meta('adresse_neuf');
 	$jours_neuf = lire_meta('jours_neuf');
diff --git a/ecrire/inc_documents.php3 b/ecrire/inc_documents.php3
index 7af2df819b..818b34bace 100644
--- a/ecrire/inc_documents.php3
+++ b/ecrire/inc_documents.php3
@@ -362,7 +362,7 @@ function fichiers_upload($dir) {
 	$fichiers = array();
 	$d = opendir($dir);
 
-	while ($f = readdir($d)) {
+	while (($f = readdir($d)) !== false) {
 		if (($f[0] != '.') AND is_readable("$dir/$f"))
 			if (is_file("$dir/$f") 
 			AND $f != 'remove.txt')
diff --git a/ecrire/inc_flock.php3 b/ecrire/inc_flock.php3
index a29199fdb0..12b3f8eff2 100644
--- a/ecrire/inc_flock.php3
+++ b/ecrire/inc_flock.php3
@@ -137,9 +137,8 @@ function supprimer_fichier($fichier) {
 // Retourne $subdir/ si le sous-repertoire peut etre cree, '' sinon
 //
 function creer_repertoire($base, $subdir) {
-	if (@file_exists("$base/.plat")) return '';
 	$path = $base.'/'.$subdir;
-	if (@file_exists($path)) return "$subdir/";
+	if (@is_dir($path)) return "$subdir/";
 
 	@mkdir($path, 0777);
 	@chmod($path, 0777);
@@ -148,14 +147,10 @@ function creer_repertoire($base, $subdir) {
 		@fputs($f, '<'.'?php $ok = true; ?'.'>');
 		@fclose($f);
 		include("$path/.test");
+		@unlink("$path/.test");
 	}
 	if (!$ok) {
-		$f = @fopen("$base/.plat", "w");
-		if ($f)
-			fclose($f);
-		else {
-			redirige_par_entete("spip_test_dirs.php3");
-		}
+		redirige_par_entete("spip_test_dirs.php3");
 	}
 	return ($ok? "$subdir/" : '');
 }
diff --git a/ecrire/inc_getdocument.php3 b/ecrire/inc_getdocument.php3
index c67179eaf4..9c8e39ba6d 100644
--- a/ecrire/inc_getdocument.php3
+++ b/ecrire/inc_getdocument.php3
@@ -37,7 +37,7 @@ function creer_repertoire_documents($ext) {
 // Efface le repertoire de maniere recursive !
 function effacer_repertoire_temporaire($nom) {
 	$d = opendir($nom);
-	while ($f = readdir($d)) {
+	while (($f = readdir($d)) !== false) {
 		if (is_file("$nom/$f"))
 			@unlink("$nom/$f");
 		else if ($f <> '.' AND $f <> '..'
@@ -594,7 +594,7 @@ function identifie_repertoire_et_rubrique($DIR, $id_rubrique, $id_auteur, $art=0
   $rubriques = array();
 
   // collecte des 3 sortes d'entrees
-  while (($entree = readdir($handle)) != '') {
+  while (($entree = readdir($handle)) !== false) {
     $chemin = "$DIR/$entree";
     if ($entree[0] !='.') {
       if (is_dir($chemin)) {
diff --git a/ecrire/inc_lang.php3 b/ecrire/inc_lang.php3
index ac485f97de..1914f450c0 100644
--- a/ecrire/inc_lang.php3
+++ b/ecrire/inc_lang.php3
@@ -403,7 +403,7 @@ function init_langues() {
 	$toutes_langs = Array();
 	if (!$all_langs || !$langue_site || !_DIR_RESTREINT) {
 		if (!$d = @opendir(_DIR_LANG)) return;
-		while ($f = readdir($d)) {
+		while (($f = readdir($d)) !== false) {
 			if (ereg('^spip_([a-z_]+)\.php3?$', $f, $regs))
 				$toutes_langs[] = $regs[1];
 		}
diff --git a/ecrire/inc_lang_raccourcis.php3 b/ecrire/inc_lang_raccourcis.php3
index 34bad02b2f..d8080f03a0 100644
--- a/ecrire/inc_lang_raccourcis.php3
+++ b/ecrire/inc_lang_raccourcis.php3
@@ -35,7 +35,7 @@ debut_gauche();
 $modules = array();
 
 if (!$d = @opendir(_DIR_LANG)) return;
-while ($f = readdir($d)) {
+while (($f = readdir($d)) !== false) {
 	if (ereg('^([a-z_]+)\.php3?$', $f, $regs))
 		$nom_module = $regs[1];
 		if (!ereg('^(spip|ecrire)\_', $nom_module) && ereg("^([a-zA-Z]+)\_".$spip_lang."$", $nom_module, $reps))
@@ -85,7 +85,7 @@ function afficher_raccourcis($module = "public") {
 		echo "<div>&nbsp;</div>";
 
 		if (!$d = @opendir(_DIR_LANG)) return;
-		while ($f = readdir($d)) {
+		while (($f = readdir($d)) !== false) {
 			if (ereg("^".$module."\_([a-z_]+)\.php3?$", $f, $regs))
 				$langue_module[$regs[1]] = traduire_nom_langue($regs[1]);
 		}
diff --git a/ecrire/inc_logos.php3 b/ecrire/inc_logos.php3
index 3e910c7eeb..d98ef146eb 100644
--- a/ecrire/inc_logos.php3
+++ b/ecrire/inc_logos.php3
@@ -133,7 +133,7 @@ function afficher_logo($racine, $titre, $logo, $id_objet, $id) {
 		$afficher = "";
 		if ($GLOBALS['flag_upload']) {
 			$myDir = opendir(_DIR_TRANSFERT);
-			while($entryName = readdir($myDir)){
+			while(($entryName = readdir($myDir)) !== false){
 				if (!ereg("^\.",$entryName) AND eregi("(gif|jpg|png)$",$entryName)){
 					$entryName = addslashes($entryName);
 					$afficher .= "\n<option value='$entryName'>$entryName</option>";
diff --git a/ecrire/inc_popularites.php3 b/ecrire/inc_popularites.php3
deleted file mode 100644
index 4347b81588..0000000000
--- a/ecrire/inc_popularites.php3
+++ /dev/null
@@ -1,81 +0,0 @@
-<?php
-
-/***************************************************************************\
- *  SPIP, Systeme de publication pour l'internet                           *
- *                                                                         *
- *  Copyright (c) 2001-2005                                                *
- *  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.   *
-\***************************************************************************/
-
-
-if (!defined("_ECRIRE_INC_VERSION")) return;
-
-//
-// Popularite, modele logarithmique
-//
-
-function calculer_popularites() {
-
-	// Si c'est le premier appel (fichier .lock absent), ne pas calculer
-	$lock = _DIR_SESSIONS . 'popularites.lock';
-	if (!$t = @filemtime($lock)) return;
-	spip_touch($lock);
-
-	$duree = time() - $t;
-	// duree de demi-vie d'une visite dans le calcul de la popularite (en jours)
-	$demivie = 1;
-	// periode de reference en jours
-	$periode = 1;
-	// $a est le coefficient d'amortissement depuis la derniere mesure
-	$a = pow(2, - $duree / ($demivie * 24 * 3600));
-	// $b est la constante multiplicative permettant d'avoir
-	// une visite par jour (periode de reference) = un point de popularite
-	// (en regime stationnaire)
-	// or, magie des maths, ca vaut log(2) * duree journee/demi-vie
-	// si la demi-vie n'est pas trop proche de la seconde ;)
-	$b = log(2) * $periode / $demivie;
-
-	// oublier un peu le passe
-	spip_query("UPDATE spip_articles SET maj=maj, popularite = popularite * $a");
-
-	// ajouter les points visites
-	$count_article = Array();
-	$query = "SELECT COUNT(*) as count,id_objet FROM spip_visites_temp WHERE maj > DATE_SUB(NOW(), INTERVAL $duree SECOND) AND type='article' GROUP BY id_objet";
-	$res = spip_query($query);
-	while ($row = @spip_fetch_array($res)) {
-		$count_article[$row['count']] .= ','.$row['id_objet'];	// l'objet a count visites
-	}
-
-	foreach($count_article as $count => $articles) {
-		$query = "UPDATE spip_articles
-			SET maj=maj, popularite = GREATEST(1,popularite) + $b * $count
-			WHERE id_article IN (0$articles)";
-		spip_query($query);
-	}
-
-	// ajouter les points referers
-	$count_article = Array();
-	$query = "SELECT COUNT(*) as count,id_objet FROM spip_referers_temp WHERE maj > DATE_SUB(NOW(), INTERVAL $duree SECOND) AND type='article' GROUP BY id_objet";
-	$res = spip_query($query);
-	while ($row = @spip_fetch_array($res)) {
-		$count_article[$row['count']] .= ','.$row['id_objet'];	// l'objet a count referers
-	}
-
-	foreach($count_article as $count => $articles) {
-		$query = "UPDATE spip_articles
-			SET maj=maj, popularite = GREATEST(1,popularite) + $b * $count
-			WHERE id_article IN (0$articles)";
-		spip_query($query);
-	}
-
-	// et enregistrer les metas...
-	list($maxpop, $totalpop) = spip_fetch_array(spip_query("SELECT MAX(popularite), SUM(popularite) FROM spip_articles"));
-	ecrire_meta("popularite_max", $maxpop);
-	ecrire_meta("popularite_total", $totalpop);
-	ecrire_metas();
-}
-
-?>
diff --git a/ecrire/inc_session.php3 b/ecrire/inc_session.php3
index 31755ce2fa..d304102a08 100644
--- a/ecrire/inc_session.php3
+++ b/ecrire/inc_session.php3
@@ -160,7 +160,7 @@ function zap_sessions ($id_auteur, $zap) {
 
 	$dir = opendir(_DIR_SESSIONS);
 	$t = time();
-	while(($item = readdir($dir)) != '') {
+	while(($item = readdir($dir)) !== false) {
 		$chemin = _DIR_SESSIONS . $item;
 		if (ereg("^session_([0-9]+_)?([a-z0-9]+)\.php3$", $item, $regs)) {
 
diff --git a/ecrire/inc_statistiques.php3 b/ecrire/inc_statistiques.php3
index 69d5acd82d..7aa76231b7 100644
--- a/ecrire/inc_statistiques.php3
+++ b/ecrire/inc_statistiques.php3
@@ -13,10 +13,6 @@
 
 if (!defined("_ECRIRE_INC_VERSION")) return;
 
-//
-// Compiler les statistiques temporaires : visites
-//
-
 // Les deux fonctions suivantes sont adaptees du code des "Visiteurs",
 // par Jean-Paul Dezelus (http://www.phpinfo.net/applis/visiteurs/)
 
@@ -144,119 +140,6 @@ function stats_show_keywords($kw_referer, $kw_referer_host) {
 }
 
 
-//
-// Optimiser les informations liees aux referers
-//
-
-function supprimer_referers($type = "") {
-	$table = 'spip_referers';
-	if ($type) {
-		$table .= '_'. $type . 's';
-		$col_id = 'id_' . $type;
-		$query = "SELECT COUNT(DISTINCT $col_id) AS count FROM $table";
-		$result = spip_query($query);
-		if ($row = @spip_fetch_array($result)) {
-			$count = $row['count'];
-		}
-	}
-	if (!$count) $count = 1;
-	$count = intval($count * 100);
-	$query = "SELECT visites FROM $table ".
-		"ORDER BY visites LIMIT 1 OFFSET $count";
-	$result = spip_query($query);
-	$visites_min =  1;
-	if ($row = @spip_fetch_array($result)) {
-		$visites_min = $row['visites'];
-	}
-
-	$query = "DELETE FROM $table WHERE (date < DATE_SUB(NOW(),INTERVAL 7 DAY) AND visites <= $visites_min) OR (date < DATE_SUB(NOW(),INTERVAL 30 DAY))";
-	$result = spip_query($query);
-}
-
-
-
-//
-// Compiler les statistiques temporaires : referers (si active)
-//
-
-function calculer_n_referers($nb_referers) {
-	$date = date("Y-m-d");
-
-	$result = spip_query("SELECT COUNT(DISTINCT ip) AS visites, referer, HEX(referer_md5) AS md5 ".
-			     "FROM spip_referers_temp GROUP BY referer_md5 LIMIT $nb_referers");
-
-	$tous = spip_num_rows($result);
-
-	$referer_insert = "";
-	$referer_update = "";
-	$referer_vus = "";
-
-	while ($row = @spip_fetch_array($result)) {
-		$visites = $row['visites'];
-		$referer = addslashes($row['referer']);
-		$referer_md5 = '0x'.$row['md5'];
-		$referer_update[$visites][] = $referer_md5;
-		$referer_insert[] = "('$date', '$referer', $referer_md5, $visites, $visites)";
-		$referer_vus .= "," . $referer_md5;
-	}
-	if ($referer_vus) 
-	  $referer_vus = "referer_md5 IN (" . substr($referer_vus,1) . ")";
-
-	// Mise a jour de la base
-	if (is_array($referer_update)) {
-		while (list($visites, $referers) = each($referer_update)) {
-			$query = "UPDATE spip_referers SET visites = visites + $visites, visites_jour = visites_jour + $visites ".
-				"WHERE referer_md5 IN (".join(', ', $referers).")";
-			$result = spip_query($query);
-		}
-	}
-	if (is_array($referer_insert)) {
-		$query_insert = "INSERT IGNORE INTO spip_referers ".
-			"(date, referer, referer_md5, visites, visites_jour) VALUES ".join(', ', $referer_insert);
-		$result_insert = spip_query($query_insert);
-	}
-
-	// Ventiler ces referers article par article
-	$query = "SELECT COUNT(DISTINCT ip) AS visites, id_objet, referer, HEX(referer_md5) AS md5 FROM spip_referers_temp WHERE type='article'"
-		  . ($referer_vus ? " AND $referer_vus" : '')
-		  . " GROUP BY id_objet, referer_md5";
-	$result = spip_query($query);
-
-	$referer_insert = "";
-	$referer_update = "";
-
-	while ($row = @spip_fetch_array($result)) {
-		$id_article = $row['id_objet'];
-		$visites = $row['visites'];
-		$referer = addslashes($row['referer']);
-		$referer_md5 = '0x'.$row['md5'];
-
-		$referer_update[$visites][] = "(id_article=$id_article AND referer_md5=$referer_md5)";
-		$referer_insert[] = "('$date', '$referer', $referer_md5, $id_article, $visites)";
-	}
-
-	// Mise a jour de la base
-	if (is_array($referer_update)) {
-		while (list($visites, $where) = each($referer_update)) {
-			$query = "UPDATE spip_referers_articles SET visites = visites + $visites ".
-				"WHERE ".join(' OR ', $where);
-			$result = spip_query($query);
-		}
-	}
-	if (is_array($referer_insert)) {
-		$query_insert = "INSERT IGNORE INTO spip_referers_articles ".
-			"(date, referer, referer_md5, id_article, visites) VALUES ".join(', ', $referer_insert);
-		$result_insert = spip_query($query_insert);
-	}
-
-	// Effacer les referers traites
-	if ($referer_vus) {
-	  spip_query("DELETE FROM spip_referers_temp WHERE $referer_vus");
-	}
-	return  $tous ;
-}
-
-
 //
 // Afficher les referers d'un article (ou du site)
 //
diff --git a/ecrire/inc_visites.php3 b/ecrire/inc_visites.php3
index cdbefa5910..f4409b5ce2 100644
--- a/ecrire/inc_visites.php3
+++ b/ecrire/inc_visites.php3
@@ -13,71 +13,254 @@
 
 if (!defined("_ECRIRE_INC_VERSION")) return;
 
-function calculer_visites() {
-
-	// La date des enregistrements de spip_visites_temp correspond a la veille
-	// du calcul.
-	$hier = date("Y-m-d", time() - 24*3600);
-
-	// Sur tout le site, nombre de visiteurs uniques pendant la periode
-	// qui precede (normalement, une journee)
-	$query = "SELECT COUNT(DISTINCT ip) AS total_visites FROM spip_visites_temp";
-	$result = spip_query($query);
-	if ($row = @spip_fetch_array($result))
-		$total_visites = $row['total_visites'];
-	else
-		$total_visites = 0;
-	spip_query("INSERT IGNORE INTO spip_visites
-		(date, visites) VALUES ('$hier', 0)");
-	spip_query("UPDATE spip_visites SET visites = visites+$total_visites
-		WHERE date='$hier'");
-
-	// Nombre de visiteurs uniques par article
-	$query = "SELECT COUNT(DISTINCT ip) AS visites, id_objet
-		FROM spip_visites_temp WHERE type='article' GROUP BY id_objet";
-	$result = spip_query($query);
-
-	$visites_insert = array();
-	$visites_update = array();
-
-	while ($row = @spip_fetch_array($result)) {
-		$id_article = $row['id_objet'];
-		$visites = $row['visites'];
-		$visites_update[$visites][] = $id_article;
+
+
+### Pour se d暫arrasser du md5, comment faire ? Un index sur 'referer' ?
+### ou alors la meme notion, mais sans passer par des fonctions HEX ?
+
+
+//
+// prendre en compte un fichier de visite
+//
+function compte_fichier_visite($fichier,
+&$visites, &$visites_a, &$referers, &$referers_a, &$articles) {
+
+	// Noter la visite du site (article 0)
+	$visites ++;
+
+	$content = array();
+	if (lire_fichier($fichier, $content))
+		$content = unserialize($content);
+
+	foreach ($content as $source => $num) {
+		list($log_type, $log_id_num, $log_referer)
+			= preg_split(",\t,", $source, 3);
+		
+		// Noter le referer
+		if ($log_referer)
+			$referers[$log_referer]++;
+
+		// S'il s'agit d'un article, noter ses visites
+		if ($log_type == 'article'
+		AND $id_article = intval($log_id_num)) {
+			$articles[] = $id_article;
+			$visites_a[$id_article] ++;
+			if ($log_referer)
+				$referers_a[$id_article][$log_referer]++;
+		}
+	}
+}
+
+
+function calculer_visites($t) {
+
+	// Initialisations
+	$visites = ''; # visites du site
+	$visites_a = array(); # tableau des visites des articles
+	$referers = array(); # referers du site
+	$referers_a = array(); # tableau des referers des articles
+	$articles = array(); # articles vus dans ce lot de visites
+
+	// charger un certain nombre de fichiers de visites,
+	// et faire les calculs correspondants
+
+	// 1. Chercher les paniers datant d'au moins 30 minutes
+	$date_init = date('YmdHi', time()-30*60);
+	$paniers = array();
+	$dir = opendir(_DIR_SESSIONS);
+	while (($item = readdir($dir)) !== false) {
+		if (preg_match(',^stats_([0-9]{12})$,', $item, $regs)
+		AND $regs[1]<$date_init
+		AND is_dir(_DIR_SESSIONS.$item))
+			$paniers[] = $item;
 	}
+	closedir($dir);
+
+	// 2. Manger 1000 fichiers de ces paniers (sans ordre particulier)
+	$compteur = 1000;
+	$pasfini = false;
+	foreach ($paniers as $panier) {
+		$dir = opendir(_DIR_SESSIONS.$panier);
+		while (($item = readdir($dir)) !== false) {
+			if (is_file($f = _DIR_SESSIONS.$panier.'/'.$item)) {
+				compte_fichier_visite($f,
+					$visites, $visites_a, $referers, $referers_a, $articles);
+				@unlink($f);
+			}
+			if (-- $compteur <= 0) {
+				$pasfini = true;
+				break;
+			}
+		}
+		// effacer le panier, sauf si on a atteint la limite de fichiers vus
+		closedir($dir);
+		if (!$pasfini)
+			@rmdir(_DIR_SESSIONS.$panier);
+	}
+
+	if (!$visites) return;
+
+	// Maintenant on dispose de plusieurs tableaux qu'il faut ventiler dans
+	// les tables spip_visites, spip_visites_articles, spip_referers
+	// et spip_referers_articles ; attention a affecter tout ca a la bonne
+	// date quand on est a cheval (entre minuit et 1 h du mat)
+	$date = date("Y-m-d", time() - 3600);
+
+	// 1. les visites du site (facile)
+	spip_query("INSERT IGNORE INTO spip_visites
+	(date) VALUES ('$date')");
+	spip_query("UPDATE spip_visites SET visites = visites+$visites
+	WHERE date='$date'");
 
-	$query_effacer = "DELETE FROM spip_visites_temp";
-	$result_effacer = spip_query($query_effacer);
-
-	// Mise a jour de la base
-	foreach ($visites_update as $visites => $articles) {
-		// Augmenter les stats totales des articles
-		spip_query("UPDATE spip_articles SET maj=maj,
-			visites = visites + $visites
-			WHERE id_article IN (".join(',', $articles).")");
-		// Inserer des visites pour la journee (si pas deja fait)
-		$insert = "('$hier',0,". join ("),('$hier',0,", $articles) . ')';
+	// pour calcul_mysql_in
+	include_ecrire('inc_db_mysql.php3');
+
+	// 2. les visites des articles (en deux passes pour minimiser
+	// le nombre de requetes)
+	if ($articles) {
+		// s'assurer qu'un slot (date, visites, id) existe pour
+		// chaque article vu
 		spip_query("INSERT IGNORE INTO spip_visites_articles
-			(date, visites, id_article) VALUES $insert");
-		// Augmenter les stats des visites de la journee
-		spip_query("UPDATE spip_visites_articles
-			SET visites=visites+$visites WHERE date='$hier'
-			AND id_article IN (".join(',', $articles).")");
+		(date, id_article) VALUES ('$date',"
+		. join("), ('$date',", $articles)
+		. ")");
+
+		// enregistrer les visites
+		$ar = array(); # tableau num -> liste des articles ayant num visites
+		foreach($visites_a as $id_article => $num)
+			$ar[$num][] = $id_article;
+		foreach ($ar as $num => $liste) {
+			spip_query("UPDATE spip_visites_articles
+			SET visites = visites+$num
+			WHERE date='$date' AND ".
+			calcul_mysql_in('id_article', join(',',$liste)));
+			## Ajouter un JOIN sur le statut de l'article ?
+		}
+	}
+
+	// 3. Les referers du site
+	if ($referers) {
+		$ar = array();
+		// s'assurer d'un slot pour chacun
+		foreach ($referers as $referer => $num) {
+			$referer_md5 = '0x'.substr(md5($referer), 0, 15);
+			$insert[] = "('$date', '".addslashes($referer)."',
+				$referer_md5)";
+			$ar[$num][] = $referer_md5;
+		}
+		spip_query("INSERT IGNORE INTO spip_referers
+			(date, referer, referer_md5) VALUES "
+			. join(', ', $insert));
+		
+		// ajouter les visites
+		foreach ($ar as $num => $liste) {
+			spip_query("UPDATE spip_referers
+			SET visites = visites+$num, visites_jour = visites_jour+$num
+			WHERE ".calcul_mysql_in('referer_md5',join(',',$liste)));
+		}
+	}
+	
+	// 4. Les referers d'articles
+	if ($referers_a) {
+		$ar = array();
+		$insert = array();
+		// s'assurer d'un slot pour chacun
+		foreach ($referers_a as $id_article => $referers)
+		foreach ($referers as $referer => $num) {
+			$referer_md5 = '0x'.substr(md5($referer), 0, 15);
+			$insert[] = "('$date', '".addslashes($referer)."',
+				$referer_md5, $id_article)";
+			$ar[$num][] = "(id_article=$id_article AND referer_md5=$referer_md5)";
+		}
+		spip_query("INSERT IGNORE INTO spip_referers_articles
+			(date, referer, referer_md5) VALUES "
+			. join(', ', $insert));
+		
+		// ajouter les visites
+		foreach ($ar as $num => $liste) {
+			spip_query("UPDATE spip_referers_articles
+			SET visites = visites+$num, visites_jour = visites_jour+$num
+			WHERE ".join(" OR ", $liste));
+			## Ajouter un JOIN sur le statut de l'article ?
+		}
 	}
 
+	// 5. Calculer les popularites ; ici c'est presque comme les visites,
+	// sauf qu'on ajoute 1 point par referer
+	if ($visites_a) {
+		$points = array();
+		foreach ($visites_a as $id_article => $v) {
+			// ajouter un point aux articles ayant un referer
+			if ($r = $referers_a[$id_article])
+				$v += 1; // ou array_pop($r);
+			$points[$v][] = $id_article;
+		}
+		foreach ($points as $num => $liste) {
+			spip_query("UPDATE spip_articles
+			SET popularite = popularite + $num
+			WHERE ".calcul_mysql_in('id_article', join(',',$liste)));
+		}
+	}
+
+	// S'il reste des fichiers a manger, le signaler pour reexecution rapide
+	return $pasfini;
+}
+
+
+
+//
+// Popularite, modele logarithmique
+//
+
+function calculer_popularites() {
+	include_ecrire('inc_meta.php3');
+
+	// Si c'est le premier appel, ne pas calculer
+	$t = lire_meta('date_popularites');
+	ecrire_meta('date_popularites', time());
+	ecrire_metas();
+	if (!$t)
+		return;
+
+	$duree = time() - $t;
+	// duree de demi-vie d'une visite dans le calcul de la popularite (en jours)
+	$demivie = 1;
+	// periode de reference en jours
+	$periode = 1;
+	// $a est le coefficient d'amortissement depuis la derniere mesure
+	$a = pow(2, - $duree / ($demivie * 24 * 3600));
+	// $b est la constante multiplicative permettant d'avoir
+	// une visite par jour (periode de reference) = un point de popularite
+	// (en regime stationnaire)
+	// or, magie des maths, ca vaut log(2) * duree journee/demi-vie
+	// si la demi-vie n'est pas trop proche de la seconde ;)
+	$b = log(2) * $periode / $demivie;
+
+	// oublier un peu le passe
+	spip_query("UPDATE spip_articles SET maj=maj, popularite = popularite * $a");
+
+	// enregistrer les metas...
+	list($maxpop, $totalpop) = spip_fetch_array(spip_query("SELECT MAX(popularite), SUM(popularite) FROM spip_articles"));
+	ecrire_meta("popularite_max", $maxpop);
+	ecrire_meta("popularite_total", $totalpop);
+
+
 	// Une fois par jour purger les referers du jour ; qui deviennent
 	// donc ceux de la veille ; au passage on stocke une date_statistiques
 	// dans spip_meta - cela permet au code d'etre "reentrant", ie ce cron
 	// peut etre appele par deux bases SPIP ne partageant pas le meme
 	// _DIR_SESSIONS, sans tout casser...
 	$aujourdhui = date("Y-m-d");
-	if ($date_referers = lire_meta('date_statistiques')
-	AND $date_referers != $aujourdhui) {
+	if ($date = lire_meta('date_statistiques')
+	AND $date != $aujourdhui) {
 		spip_query("UPDATE spip_referers SET visites_veille=visites_jour, visites_jour=0");
 	}
 	ecrire_meta('date_statistiques', $aujourdhui);
+
+	// et c'est fini pour cette fois-ci
 	ecrire_metas();
 	return 1;
+
 }
 
 ?>
diff --git a/formulaires/inc-formulaire_admin.php3 b/formulaires/inc-formulaire_admin.php3
index 2c5169f07a..c6b876a1fd 100644
--- a/formulaires/inc-formulaire_admin.php3
+++ b/formulaires/inc-formulaire_admin.php3
@@ -152,4 +152,24 @@ function balise_FORMULAIRE_ADMIN_dyn($float='', $debug='') {
 			)
 		);
 }
+
+// Un outil pour le bouton d'amin "statistiques"
+function afficher_raccourci_stats($id_article) {
+	$query = "SELECT visites, popularite FROM spip_articles WHERE id_article=$id_article AND statut='publie'";
+	$result = spip_query($query);
+	if ($row = @spip_fetch_array($result)) {
+		$visites = intval($row['visites']);
+		$popularite = ceil($row['popularite']);
+
+		$query = "SELECT COUNT(DISTINCT ip) AS c FROM spip_visites_temp WHERE type='article' AND id_objet=$id_article";
+		$result = spip_query($query);
+		if ($row = @spip_fetch_array($result)) {
+			$visites = $visites + $row['c'];
+		}
+
+		return array('visites' => $visites, 'popularite' => $popularite);
+	}
+}
+
+
 ?>
diff --git a/inc-cache.php3 b/inc-cache.php3
index d8509d8130..16e8c54e03 100644
--- a/inc-cache.php3
+++ b/inc-cache.php3
@@ -167,7 +167,7 @@ function purger_repertoire($dir, $age='ignore', $regexp = '') {
 	$handle = @opendir($dir);
 	if (!$handle) return;
 
-	while (($fichier = @readdir($handle)) != '') {
+	while (($fichier = @readdir($handle)) !== false) {
 		// Eviter ".", "..", ".htaccess", etc.
 		if ($fichier[0] == '.') continue;
 		if ($regexp AND !ereg($regexp, $fichier)) continue;
@@ -241,7 +241,7 @@ function calculer_taille_dossier ($dir) {
 	$handle = @opendir($dir);
 	if (!$handle) return;
 
-	while (($fichier = @readdir($handle)) != '') {
+	while (($fichier = @readdir($handle)) !== false) {
 		// Eviter ".", "..", ".htaccess", etc.
 		if ($fichier[0] == '.') continue;
 		if ($regexp AND !ereg($regexp, $fichier)) continue;
@@ -257,7 +257,7 @@ function calculer_cache_vignettes() {
 	$handle = @opendir(_DIR_IMG);
 	if (!$handle) return;
 
-	while (($fichier = @readdir($handle)) != '') {
+	while (($fichier = @readdir($handle)) !== false) {
 		// Eviter ".", "..", ".htaccess", etc.
 		if ($fichier[0] == '.') continue;
 		if ($regexp AND !ereg($regexp, $fichier)) continue;
diff --git a/inc-stats.php3 b/inc-stats.php3
index 5a3b43b5d6..ae6e80a17b 100644
--- a/inc-stats.php3
+++ b/inc-stats.php3
@@ -32,50 +32,50 @@ function ecrire_stats() {
 		$log_type = "autre";
 
 	// Conversion IP 4 octets -> entier 32 bits
-	if (ereg("^(::ffff:)?([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$", $log_ip, $r)) {
-		$log_ip = sprintf("0x%02x%02x%02x%02x", $r[2], $r[3], $r[4], $r[5]);
-	}
-	else return;
+	if (preg_match(",^(::ffff:)?([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$,",
+	$log_ip, $r))
+		$log_ip = sprintf("%02x%02x%02x%02x", $r[2], $r[3], $r[4], $r[5]);
+	else
+		return;
 
-	//
-	// Loguer la visite dans la base si possible
-	//
-	if ($log_type != "autre") {
-		$query = "INSERT IGNORE INTO spip_visites_temp (ip, type, id_objet) ".
-			"VALUES ($log_ip, '$log_type', $log_id_num)";
-		spip_query($query);
+	// Analyse du referer
+	if ($log_referer = $GLOBALS['HTTP_REFERER']) {
+		$url_site_spip = preg_replace(',^((https?|ftp)://)?(www\.)?,i', '',
+			lire_meta('adresse_site'));
+		if (($url_site_spip<>'')
+		AND strpos('-'.strtolower($log_referer), strtolower($url_site_spip))
+		AND !$_GET['var_recherche'])
+			$log_referer = '';
+		else
+			$referer_md5 = '0x'.substr(md5($log_referer), 0, 15);
 	}
 
 	//
-	// Loguer le referer
+	// stockage sous forme de fichier dans ecrire/data/stats_200511161005/ip
 	//
-	$url_site_spip = lire_meta('adresse_site');
-	$url_site_spip = eregi_replace("^((https?|ftp)://)?(www\.)?", "", $url_site_spip);
-	$log_referer = $GLOBALS['HTTP_REFERER'];
-	if (($url_site_spip<>'') AND strpos('-'.strtolower($log_referer), strtolower($url_site_spip)) AND !$GLOBALS['var_recherche']) $log_referer = "";
-	if ($log_referer) {
-		$referer_md5 = '0x'.substr(md5($log_referer), 0, 15);
-		$query = "INSERT IGNORE INTO spip_referers_temp (ip, referer, referer_md5, type, id_objet) ".
-			"VALUES ($log_ip, '".addslashes($log_referer)."', $referer_md5, '$log_type', $log_id_num)";
-		spip_query($query);
-	}
-}
-
-
-function afficher_raccourci_stats($id_article) {
-	$query = "SELECT visites, popularite FROM spip_articles WHERE id_article=$id_article AND statut='publie'";
-	$result = spip_query($query);
-	if ($row = @spip_fetch_array($result)) {
-		$visites = intval($row['visites']);
-		$popularite = ceil($row['popularite']);
 
-		$query = "SELECT COUNT(DISTINCT ip) AS c FROM spip_visites_temp WHERE type='article' AND id_objet=$id_article";
-		$result = spip_query($query);
-		if ($row = @spip_fetch_array($result)) {
-			$visites = $visites + $row['c'];
+	// 1. Chercher dans les paniers recents (moins de 30 minutes) s'il existe
+	// deja une session pour ce numero IP. Chaque panier couvre 5 minutes
+	$content = array();
+	for ($i = -5; $i <= 0; $i++) {
+		$panier = date('YmdHi', (intval(time()/300)+$i)*300);
+		if (@file_exists($s = _DIR_SESSIONS.'stats_'.$panier.'/'.$log_ip)) {
+			lire_fichier($s, $content);
+			$content = @unserialize($content);
+			if ($i<0) @unlink($s);
+			break;
 		}
+	}
+
+	// 2. Determiner le fichier session dans le panier actuel
+	$panier = date('YmdHi', (intval(time()/300))*300);
+	$dir = _DIR_SESSIONS.creer_repertoire(_DIR_SESSIONS,'stats_'.$panier);
 
-		return array('visites' => $visites, 'popularite' => $popularite);
+	// 3. Plafonner le nombre de hits pris en compte pour un IP (robots etc.)
+	if (count($content) < 200) {
+		$entree = trim("$log_type\t$log_id_num\t$log_referer");
+		$content[$entree] ++;
+		ecrire_fichier($s, serialize($content));
 	}
 }
 
-- 
GitLab