Sponsored by NuSphere - PHP Software for PHP Application Developers - On Sale This Week for $100 off


PHP Tutorials and Scripts   




Title: Simple graphical daily hit and host counter    Marked Cool    (Review this resource)
Author: kaklz
Posted On: 2004-11-24
Category: Home > PHP Tutorials

Popularity: 2 points out of 10    

Description: Ever wondered how to create a simple and nice-looking graphical daily hit/host counter? Already know how to do it? Anyway, you can check how I created one.

Total Hits: 3209     Total Votes: 6     Total Points: 28 (7 reviews)    

Page Navigation:  [1]


Simple graphical daily hit/host counter


About author

Ingus Rukis, http://www.wisecms.com, ingus.rukis@gmail.com. I've been working as a PHP programmer for a while (some 3 years, I guess) and on my spare time I enjoy writing some simple and easy tutorials for beginners. I mostly write in Latvian, that's my mother language, so if you notice some spelling or grammar mistakes, please excuse me for that.

Introduction

There are a lot of counter systems out there, most of them offer you graphical counters, statistics of your website visitors and many other nice features. Here in Latvia, we have a couple of local counter systems, and one of the most popular and precise is called Hackers.lv counter system. One of the possible reasons of popularity is that they don't put any logos on counters - it just displays daily hits and hosts. So this tutorial will show, how to create a simple graphical daily hit/host counter like the one Hackers.lv counter system offers.

Requirements and warnings

Web server, PHP with GD support, some HTML and file permission knowledge for setting up the counter.

This counter could be slow on high traffic web sites - the counter image is generated on the fly and the whole list of daily hit IP addresses are read to memory.

Saving data

In order to know how many hosts (unique IP addreses) your pages have received each day, we need to save all IP addresses, which have accessed one or another page, where the counter is placed. The most easy way of doing that could be saving all the IP addresses of current day in some text file, which would have name that corresponds to some date format.

Let's use files with names like YYYY-mm-dd.txt, where YYYY is 4 digits for year, mm - 2 digits for month and dd - 2 digits for day. Those of you, who are familiar with PHP's Date function already scream out that they do know how to create a file with that name:

$ipListFile = date('Y-m-d') . '.txt'

For example, 2004-11-24.txt, 2004-11-25.txt, etc.

As we expect to have more than one visitor per day, we will separate the saved IP addresses by newline characters - we will save IP of each visitor in a new line.

In order to be able to separate counter data from other scripts, we could create some 'data' directory, where all the files with IP addresses will be saved.

So, what's the overall progress of the counter? Let's put it all together:

// data directory
define('DATA_DIR', '/home/user/public_html/counter/data/');
// daily IP addresses file
$ipListFile = DATA_DIR . date('Y-m-d') . '.txt';

The next step would be getting the IP address of visitor and writing it to the daily hit file. As some of the visitors don't have real IP adresses, let's try to check out, if they are not behind some proxy. We can check that by looking at environment variable HTTP_X_FORWARDED_FOR. Environment variables are checked using getenv() function:

// checking IP address of visitor, even if he is behind proxy (of course, 
// if the proxy is configured to show the visitors real IP)
if(getenv("HTTP_X_FORWARDED_FOR")!=''){
	$ip= getenv('REMOTE_ADDR') . ', ' . getenv("HTTP_X_FORWARDED_FOR");
}else{
	$ip=getenv('REMOTE_ADDR');
}

So, if a visitor is behind a proxy server and the proxy server let's you know that, we save the IP of the visitor as well as the IP of the proxy server.

The data saving can be made with the file opening, appending and closing functions:

// let's add the current visitor to our list
// open the file in 'append' mode - new data is automatically added to the end of file.
// if the file does not exist, a new file is created
$fp = fopen($ipListFile, 'a');
// write data to file and add a newline character at the end
fwrite($fp, $ip . "\n");
// close the file
fclose($fp);
Data handling

The next step is reading and processing the existing information, in order to get the hit and host count.

As we don't expect very high trafic for our web site, we can read all the file in memory as an array:

// read file contents into array, each row is an array item
$ipList = file($ipListFile);

The array structure would be something similar to this one:

$ipList =  array('127.0.0.1', '192.168.0.45', '192.168.0.45');

Those of you, who are familiar with array functions, will know how to get the unique array items - the unique IP addresses of visitors. Those of you, who don't - let me introduce you to array_unique function. This function returns array without any duplicate values. So if we would process our sample array with array_unique, we would get:

$uniqueIPs = array_unique($ipList);
$uniqueIPs == array('127.0.0.1', '192.168.0.45');

So, if we have an array where we have all visitor IPs, even duplicate ones and an array with just unique IPs, we can get the host and hit counts:

// let's count hits
$hitsToday = count($ipList);
// let's count hosts
$hostsToday = count(array_unique($ipList));
Displaying information

Although it would be just about trivial to display the hit and host count in plain text, we will create a png image on the fly. I doubt any of you are familiar how the original Hackers.lv counter looks like. The look is simple: . Image is 57 pixels wide, on the left part you can see number of unique IPs and on the right side - the number of daily hits. There are only two colors used - the hits are displayed on dark background with light text and hosts - on light background with dark text.

In order to simplify the definition of colors, we will use hexadecimal represenation of colors. Hexadecimal representation is the most popular among people who build web sites. So, let's define those two colors:

$col1 = '000000';
$col2 = 'FFFFFF';

Don't forget to put these definitions somewhere in the beginning, so you can change them easily and adapt to the design of your web site.

In order to convert the colors from the hexadecimal representation to the way we need them, let's create a function that does this for us:

function getDec($col, $offset){
	$c = substr($col, $offset * 2, 2);
	return hexdec($c);
}

The function converts the 2 hexadecimal digits that represent each of the three colors (red, green, blue) to decimal system. The offset variable will be 0 for red, 1 for green and 2 for blue.

So the last thing is to hack around the GD library and build a picture with the daily hit and host counts on it.

Despite the original counter image is only 57 pixels wide, we will create the image with dimensions 60x10 pixels, in order to make the math more simple:

 $im = imagecreate(60, 10);

Next step is to allocate the colors we will use:

$textcolor = imagecolorallocate($im, getDec($col1,0), getDec($col1,1), getDec($col1,2));
$bg = imagecolorallocate($im, getDec($col2,0), getDec($col2,1), getDec($col2,2));

In this snippet we can also see the usage of getDec() function that we wrote a moment ago.

As you could see in the sample image above, half of the image is in one color and the rest - in another. Let's fill half of the image with one of the colors:

 imagefilledrectangle($im, 0, 0, 30, 10, $bg);

Don't give up now, we are quite close to the finish :). Let's put the number of hosts on the image with imagestring function:

imagestring($im, 1, 2, 1, $hostsToday , $textcolor);

We use the built-in fonts, as that's the way the original image was built.

.. and let's put the number of hits on the other part of image:

imagestring($im, 1, 33, 1, $hitsToday, $bg);

Let's return to the sample image we have - do you see the zeroes padded to the numbers? Let's do the same and edit the rows that are putting the hit and host count on the image:

imagestring($im, 1, 33, 1, str_pad($hitsToday, 5, '0', STR_PAD_LEFT), $bg);
imagestring($im, 1, 2, 1, str_pad($hostsToday, 5, '0', STR_PAD_LEFT) , $textcolor);

So the last thing to do is tell the browser that we have an image not a HTML document:

header("Content-type: image/png");

We can now safely output the image:

imagepng($im);

We could use a JPG as the output format, but the JPG format by default uses compression and the image is not as sharp as with PNG.

Setting up and using our brand new counter

As we use PHP's writing to file functions, we have to be sure that PHP has writing permissions to the data directory. I do it like this:

cd /home/user/public_html/counter
mkdir data
chown apache:apache data
chmod 0700 data

The example above works on some linux machines (tested on Gentoo linux) with root privileges.

Usually it's just enough to allow write permissions for data directory by setting it's attributes to 755.

The second and the final step of counter setup is adding the following HTML code to your web pages:

<img src="counter.php" alt="counter" title="That's MY counter, I just made it! 
Hosts on the left, hits on the right." />
Troubleshooting Problem 1

I'm seeing something like this:

Warning: Cannot modify header information - headers already sent by 
(output started at /home/9d.eclub.lv/public_html/counter.php:9) in 
/home/9d.eclub.lv/public_html/counter.php on line 149
%PNG...

Where is the problem?

Solution

Please make sure that there is no output before the output of image in counter.php file. Actually the output of image should be the one and only output in counter.php. Remember that even one space before the opening <?php tag counts as output.

Problem 2

I'm seeing something like this:

"Warning: fopen(2004-05-29.txt): failed to open stream: No such file 
or directory in C:\www\counter.php on line 40"

Where is the problem?

Solution

Probably you have not specified or left empty the data directory (DATA_DIR) constant. Please refer to the source below:

// data directory
define('DATA_DIR', '/var/www/html/tufta/hackers.lv/data/');
// daily hit file
$ipListFile = DATA_DIR . date('Y-m-d') . '.txt';

And don't forget to edit the path up to your web server

Final notes

That's it! Making this script took me about half-an hour, a bit more for this tutorial. If you don't need any detailed statistics or don't want to participate in any rating systems, then a counter like this could be just about what you need.

If you liked this tutorial, please feel free to rate it and send me any comments or questions to ingus.rukis@gmail.com

Those of you who have missed something can look at the full source code below.

Complete source code

<?php
    
    
// colors that we will use in our counter
    
$col1 = '000000';
    
$col2 = 'FFFFFF';
    
    
// function that extracts red, green and blue colors from hexadecimal
    // representation and converts them to decimal system
    
function getDec($col, $offset){
        
$c = substr($col, $offset * 2, 2);
        return
hexdec($c);
    }
    
    
// data directory
    
define('DATA_DIR', '/home/user/public_html/data/');
    
    
// daily hit file
    
$ipListFile = DATA_DIR . date('Y-m-d') . '.txt';
    
    
// IP address of visitor, even if he is behind proxy
    
if(getenv("HTTP_X_FORWARDED_FOR")!=''){
        
$ip= getenv('REMOTE_ADDR') . ', ' . getenv("HTTP_X_FORWARDED_FOR");
    }else{
        
$ip=getenv('REMOTE_ADDR');
    }
    
    
// add this visit to the file
    
$fp = fopen($ipListFile, 'a');
    
fwrite($fp, $ip . "\n");
    
fclose($fp);
    
    
// read visitor IP list file into an array
    
$ipList = file($ipListFile);
    
    
// count hits
    
$hitsToday = count($ipList);
    
    
// count hosts
    
$hostsToday = count(array_unique($ipList));
    
    
// create image
    
$im = imagecreate(60, 10);
    
    
// allocate colors
    
$textcolor = imagecolorallocate($im, getDec($col1,0), getDec($col1,1), getDec($col1,2));
    
$bg = imagecolorallocate($im, getDec($col2,0), getDec($col2,1), getDec($col2,2));
    
    
// paint half of the image in background color
    
imagefilledrectangle($im, 0, 0, 30, 10, $bg);
    
// print the number of hosts on the image
    
imagestring($im, 1, 2, 1, str_pad($hostsToday, 5, '0', STR_PAD_LEFT) , $textcolor);
    
// print the number of hits on the image
    
imagestring($im, 1, 33, 1, str_pad($hitsToday, 5, '0', STR_PAD_LEFT), $bg);
    
    
// pass the image to browser
    
header("Content-type: image/png");
    
imagepng($im);
?>


Page Navigation:  [1]



© Copyright 2003-2008 www.php-editors.com. The ultimate PHP Editor and PHP IDE site.