par Alexandre Alapetite le 2005-02-27 ; mise à jour 2008-03-28

Compteur de visites en PHP/HTTP

Je vais présenter sur cette page un système de compteur de visites léger et relativement fiable. Ce système est programmé en PHP, en utilisant le mécanisme de cache et de requêtes conditionnelles de HTTP.

English

Exemple

Cet exemple génère une image PNG

Nombre approximatif de visiteurs de cette page : [Compteur]


Installation

La procédure d'installation consiste à télécharger le script et une librairie, à les mettre dans un même répertoire, sur un serveur Web supportant PHP, et à renommer leur extension en .php

  1. Téléchargez le script du compteur (1.3)   (2008-03-28)
  2. Téléchargez la librairie pour les requêtes HTTP conditionnelles   (plus d'informations)

Il ne reste plus qu'à s'assurer que PHP a les droits en lecture et écriture dans le répertoire où ces deux fichiers ont été placés.
En particulier, le script va sauver le nombre de visites dans le fichier meter.txt, et créer à chaque exécution un fichier temporaire mutex2.txt qui est supprimé dès la fin du script, quelques millisecondes plus tard.


Tester

Pour tester que cela fonctionne, accédez via votre navigateur Web au fichier meter.png.php
Si vous testez en local, l'adresse sera de la forme http://localhost/compteur/meter.png.php

Si cela ne fonctionne pas, mettez temporairement les deux lignes error_reporting(E_ERROR); et header('Content-type: image/png'); en commentaires afin de voir les éventuels messages d'erreurs. En faisant cela, il est normal de voir une séquence de caractères étranges commençant par ‰PNG ou similaire.

Lire ma documentation sur la configuration PHP sous Apache pour plus d'explications.


Utiliser

Une fois que le compteur fonctionne seul, il est temps de l'inclure dans une page Web. Cela se fait simplement en ajoutant une image, dont l'adresse est celle du script du compteur.

<html>
 ...
 <img src="meter.png.php" height="18" alt="[Compteur]" title="Nombre de visites" />
 ...
</html>

La largeur de l'image est automatique, et augmentera en fonction des visites.


Code

Voici le code source du script du compteur.

meter.png.php

<?php
$session=1800; //En secondes
$fmeter='meter.txt';

error_reporting(E_ERROR);
header('Content-type: image/png');
require_once('http-conditional.php');
//Prenez cette librairie sur https://alexandre.alapetite.fr/doc-alex/php-http-304/

$begin=time();
$lastModif=max($begin-$session,filemtime($fmeter));
if (httpConditional($lastModif,$session,0,true,true))
 exit(); //Cache 30 minutes, privé, compression

ignore_user_abort(true);
//<obtenir_mutex>
$mutexFile1='mutex1.txt'; //Fichier à utiliser pour la méthode mutex#1
$mutexFile2='mutex2.txt'; //Fichier à utiliser pour la méthode mutex#2
$mutexOK=false;
$mutexMethod=0;
if (file_exists($mutexFile1))
{//Méthode mutex#1 (meilleure, mais ne marche pas sur beaucoup de systèmes)
 $mutexMethod=1;
 $fp=fopen($mutexFile1,'w+');
 $mutexOK=flock($fp,LOCK_EX);
}
else
{//Méthode mutex#2 (suppose fopen() atomique)
 $mutexMethod=2;
 if (file_exists($mutexFile2)&&($begin-filemtime($mutexFile2)>30))
 {//Dans le cas où une vieille exécution n'a pas pu supprimer le fichier mutex.
  //Supporte les processus concurrents
  @chmod($mutexFile2,0666);
  unlink($mutexFile2);
 }
 $attempt=1;
 while (($attempt<50)&&(@fopen($mutexFile2,'x')===false))
 {
  $begin2=time();
  usleep($attempt*5000); //$attempt*10 millisecondes
  $usleep2=1000000;
  while (($usleep2>0)&&(time()-$begin2>$attempt*5000))
   $usleep2--; //si usleep() ne marche pas
  $attempt++;
 }
 $mutexOK=($attempt<50);
}
//</obtenir_mutex>
if ($mutexOK)
{
 if($myMeter=fopen($fmeter,'r'))
 {//Lit le compteur courant
  $meter=trim(fgets($myMeter,22));
  $lastIP=trim(fgets($myMeter,36));
  fclose($myMeter);
  if (!is_numeric($meter))
   $meter=0;
 }
 else
 {
  $meter=0;
  $lastIP=md5('127.0.0.0');
 }
 $IP=md5($_SERVER['REMOTE_ADDR']);
 if (((time()-$clientCacheDate)>$session)&&
     !(($clientCacheDate<632003440)&&($IP==$lastIP)))
     //Légère protection contre les raffraîchissements abusifs
 {//Si la session a expiré, incrémente le compteur
  $meter++;
  if($myMeter=fopen($fmeter,'w'))
  {
   fputs($myMeter,$meter."\n");
   fputs($myMeter,$IP."\n");
   fclose($myMeter);
  }
  clearstatcache();
  //Met à jour les entêtes HTTP avec les nouveaux "Last-Modified" et "ETag"
  httpConditionalRefresh(filemtime($fmeter));
 }
}
else $meter='?';
//<rendre_mutex>
switch ($mutexMethod)
{
 case 1:
  flock($fp,LOCK_UN);
  fclose($fp);
  break;
 case 2:
  @chmod($mutexFile2,0666);
  unlink($mutexFile2);
  break;
}
//</rendre_mutex>
$font=5;
$im=imagecreate(5+2*$font*strlen($meter),18);
$bg=imagecolorallocate($im,255,255,255);
imagecolortransparent($im,$bg);
$tc=imagecolorallocate($im,0,0,0);
imagestring($im,$font,4,2,$meter,$tc);
imagepng($im);
imagedestroy($im);
?>

Licence

Ce contenu est protégé par une licence Creative Commons Paternité - Partage des Conditions Initiales à l'Identique 2.0 France "BY-SA (FR)" [Creative Commons License]


Commentaires

object : Voir les commentaires

https://alexandre.alapetite.fr

Retour