by Alexandre Alapetite on 2005-02-27; updated on 2008-03-28

Meter of visitors in PHP/HTTP

I will introduce on this page a meter system, which is light and relatively reliable. It is programmed with PHP, using the HTTP cache and conditional requests mechanism.

français

Example

This example generates a PNG picture.

Estimated amount of visitors of this page: [Meter]


Installation

The installation procedure consist in downloading the script and one library, to put them in one same directory, on a Web server running PHP, and to change their extension to .php

  1. Download the script of the meter (1.3)   (2008-03-28)
  2. Download the library for HTTP conditional requests   (more info)

It is now needed to ensure that PHP is allowed to read and write in the directory where those two files have been places.
In particular, the script will save the amount of visitors in the file meter.txt, and create during each execution a temporary file mutex2.txt that is erased as soon as the script ends, some milliseconds later.


Testing

In order to test that everything is working, use your Web browser to see the file meter.png.php
If testing locally, the address will look like http://localhost/test/meter.png.php

If it does not work, comment temporarily the two lines error_reporting(E_ERROR); and header('Content-type: image/png'); in order to see some possible error messages. When doing that, it is normal to see some strange text beginning by ‰PNG or similar.


Utilisation

Once the meter is working, alone, it is time to include it in a Web page. This is done simply by adding a picture, whose address is the one of the meter script.

<html>
 ...
 <img src="meter.png.php" height="18" alt="[Meter]" title="Number of visitors" />
 ...
</html>

The width of the picture is automatic, and will increase when needed.


Code

Here is the code of the script for the meter.

meter.png.php

<?php
$session=1800; //In seconds
$fmeter='meter.txt';

error_reporting(E_ERROR);
header('Content-type: image/png');
require_once('http-conditional.php');
//Details about this library on 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, private, compression

ignore_user_abort(true);
//<aquire_mutex>
$mutexFile1='mutex1.txt'; //File to use for mutex method#1
$mutexFile2='mutex2.txt'; //File to use for mutex method#2
$mutexOK=false;
$mutexMethod=0;
if (file_exists($mutexFile1))
{//Mutex method#1 (best, but does not work on many systems)
 $mutexMethod=1;
 $fp=fopen($mutexFile1,'w+');
 $mutexOK=flock($fp,LOCK_EX);
}
else
{//Mutex method#2 (assumes fopen() is atomic)
 $mutexMethod=2;
 if (file_exists($mutexFile2)&&($begin-filemtime($mutexFile2)>30))
 {//In case a previous old execution failed to clean the mutex file.
  //Resistant on concurrent processes
  @chmod($mutexFile2,0666);
  unlink($mutexFile2);
 }
 $attempt=1;
 while (($attempt<50)&&(@fopen($mutexFile2,'x')===false))
 {
  $begin2=time();
  usleep($attempt*5000); //Sleep in milliseconds
  $usleep2=1000000;
  while (($usleep2>0)&&(time()-$begin2>$attempt*5000))
   $usleep2--; //If usleep() does not work
  $attempt++;
 }
 $mutexOK=($attempt<50);
}
//</aquire_mutex>
if ($mutexOK)
{
 if($myMeter=fopen($fmeter,'r'))
 {//Read curent meter
  $meter=trim(fgets($myMeter,12));
  $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)))
     //Small protection against multiple refresh
 {//If the session has expired, increment the meter
  $meter++;
  if($myMeter=fopen($fmeter,'w'))
  {
   fputs($myMeter,$meter."\n");
   fputs($myMeter,$IP."\n");
   fclose($myMeter);
  }
  clearstatcache();
  //Update HTTP headers with new "Last-Modified" and "ETag"
  httpConditionalRefresh(filemtime($fmeter));
 }
}
else $meter='?';
//<release_mutex>
switch ($mutexMethod)
{
 case 1:
  flock($fp,LOCK_UN);
  fclose($fp);
  break;
 case 2:
  @chmod($mutexFile2,0666);
  unlink($mutexFile2);
  break;
}
//</release_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

This content is protected by a licence Creative Commons Attribution-ShareAlike 2.0 France "BY-SA (FR)" [Creative Commons License]


Comments

object: View comments

https://alexandre.alapetite.fr

Back