When creating a website, one main goal is to attract visitors. Traffic generation is a necessity for monetary purposes, showing off your work, or just expressing your thoughts. There are many ways to create traffic for your website. Search engines, social bookmarking, and word of mouth are just a few examples. But how do you know whether this traffic is genuine? How do you know if your visitors are coming back for a second time?
These questions have raised the concept of web statistics. Often times, webmasters use certain programs, such as Google Analytics or Awstats, to complete this job for them. These programs obtain a wide variety of information about visitors to a site. They find page views, visits, unique visitors, browsers, IP addresses, and much more. But how exactly is this accomplished? Follow along as we present a tutorial on how to create your own web statistics program using PHP, JavaScript, AJAX, and SQLite.
View Demo of your Own Stats Program
To begin, let’s start with some simple HTML markup that will act as the page we are tracking:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>Web Statistics</title> </head> <body> <h2 id="complete"></h2> </body> </html>
The h2#complete
element will be filled dynamically with JavaScript once the page view has successfully been tracked by our web statistics. To initiate this tracking, we can use jQuery and an AJAX request:
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script> <script type='text/javascript'> $(function() { // Set the data text var dataText = 'page=<?php echo $_SERVER['REQUEST_URI']; ?>&referrer=<?php echo $_SERVER['HTTP_REFERER']; ?>'; // Create the AJAX request $.ajax({ type: "POST", // Using the POST method url: "/process.php", // The file to call data: dataText, // Our data to pass success: function() { // What to do on success $('#complete').html( 'Your page view has been added to the statistics!' ); } }); }); </script>
Consider the above code step by step:
- When the DOM is ready, we first set our
dataText
. This text is in query string format and will be sent as data toprocess.php
, which will track this page view. - We then create an AJAX request that uses the POST method to send form data.
- Our form data (dataText) is then sent to
process.php
in the root of our server. - Once this request is completed, the
h2#complete
element is filled with a successful notice
Our next job is to write process.php
. Its main goal is to obtain information about the web statistics and store it in a database. Since our database has not yet been made, we must create a simple file, install.php
, which will do this for us:
<?php # Open the database $handle = sqlite_open( $_SERVER['DOCUMENT_ROOT'].'stats.db', 0666, $sqliteError ) or die( $sqliteError ); # Set the command to create a table $sqlCreateTable = "CREATE TABLE stats(page text UNIQUE, ip text, views UNSIGNED int DEFAULT 0, referrer text DEFAULT '')"; # Execute it sqlite_exec( $handle, $sqlCreateTable ); # Print that we are done echo 'Finished!'; ?>
This code is mostly straightforward. It opens up a database (stats.db) in the root of the server and creates a database for it. The string inside $sqlCreateTable
is a SQLite command that makes our statistics table. This table contains four colums: Page, IP Address, Views, and Referrer:
- Page is a String that contains the page being viewed as a relative link (i.e. /index.php).
- IP Address is also a String that contains a list of IP Addresses that visited this page. It is in the format of
numVisits1(IP Address1) numVisits2(IP Address2) numVisits3(IP Address3) etc
. For example, if we obtained 10 visits from 74.35.286.15 and 5 visits from 86.31.23.78 (hypothetical ip addresses), this String would be"10(74.25.286.15) 5(86.31.23.78)"
. - Views is an integer containing the number of times the page has been viewed.
- Referrer is a String in the same format as IP Address. It contains all referrers to this page and how many referrals have been made.
And now onto process.php
:
# Connect to the database $handle = sqlite_open( $_SERVER['DOCUMENT_ROOT'].'/stats.db', 0666, $sqliteError ) or die( $sqliteError ); # Use the same-origin policy to prevent cross-site scripting (XSS) attacks # Remember to replace http://yourdomain.com/ with your actual domain if( strpos( $_SERVER['HTTP_REFERER'], 'http://yourdomain.com/' ) !== 0 ) { die( "Do not call this script manually or from an external source." ); } # Obtain the necessary information, strip HTML tags, and escape the string for backup proetection $page = sqlite_escape_string( strip_tags( $_POST['page'] ) ); $referrer = sqlite_escape_string( strip_tags( $_POST['referrer'] ) ); $ip = sqlite_escape_string( strip_tags( $_SERVER['REMOTE_ADDR'] ) ); # Query the database so we can update old information $sqlGet = 'SELECT * FROM stats WHERE page = \''.$page.'\''; $result = sqlite_query( $handle, $sqlGet );
This first snippet connects to our stats database and gets the information we need for the current visit. It also queries the database and obtains any information previously stored. We use this information to create an updated table.
The next job is to find old information:
# Set up a few variables to hold old information $views = 0; $ips = ''; $referrers = ''; # Check if old information exists if( $result && ( $info = sqlite_fetch_array( $result ) ) ) { # Get this information $views = $info['views']; $ips = $info['ip'].' '; if( $info['referrer'] ) $referrers = $info['referrer'].' '; # Set a flag to state that old information was found $flag = true; }
The above code finds all previous information in our table. This is vital, as we need to update the number of views (increase it by one), IP addresses, and referrers.
# Create arrays for all referrers and ip addresses $ref_num = array(); $ip_num = array(); # Find each referrer $values = split( ' ', $referrers ); # Set a regular expression string to parse the referrer $regex = '%(\d+)\((.*?)\)%'; # Loop through each referrer foreach( $values as $value ) { # Find the number of referrals and the URL of the referrer preg_match( $regex, $value, $matches ); # If the two exist if( $matches[1] && $matches[2] ) # Set the corresponding value in the array ( referrer link -> number of referrals ) $ref_num[$matches[2]] = intval( $matches[1] ); } # If there is a referrer on this visit if( $referrer ) # Add it to the array $ref_num[$referrer]++; # Get the IPs $values = split( ' ', $ips ); # Repeat the same process as above foreach( $values as $value ) { # Find the information preg_match( $regex, $value, $matches ); # Make sure it exists if( $matches[1] && $matches[2] ) # Add it to the array $ip_num[$matches[2]] = intval( $matches[1] ); } # Update the array with the current IP. $ip_num[$ip]++;
The above two loops are very similar. They take in the information from the database and parse it with regular expressions. Once this information is parsed, it is stored in an array. Each array is then updated with the information from the current visit. This information can then be used to create the final String:
# Reset the $ips string $ips = ''; # Loop through all the information foreach( $ip_num as $key => $val ) { # Append it to the string (separated by a space) $ips .= $val.'('.$key.') '; } # Trim the String $ips = trim( $ips ); # Reset the $referrers string $referrers = ''; # Loop through all the information foreach( $ref_num as $key => $val ) { # Append it $referrers .= $val.'('.$key.') '; } # Trim the string $referrers = trim( $referrers );
The final strings are now created. IPs and Referrers are in the form: “numVisits1(IP/Referrer1) numVisits2(IP/Referrer2) etc.” For example, the following could be the referrer String:
5(https://www.noupe.com) 10(http://css-tricks.com)
It means that 5 referrals came from https://www.noupe.com and 10 came from http://css-tricks.com. This format saves space and is easy to parse as web statistics.
Now for the final few lines:
# Update the number of views $views++; # If we did obtain information from the database # (the database already contains some information about this page) if( $flag ) # Update it $sqlCmd = 'UPDATE stats SET ip=\''.$ips.'\', views=\''.$views.'\', referrer=\''.$referrers.'\' WHERE page=\''.$page.'\''; # Otherwise else # Insert a new value into it $sqlCmd = 'INSERT INTO stats(page, ip, views, referrer) VALUES (\''.$page.'\', \''.$ips.'\',\''.$views.'\',\''.$referrers.'\')'; # Execute the commands sqlite_exec( $handle, $sqlCmd );
That’s all there is to it for process.php
. As a review, it finds the IP Address and Referrer of the visitor, uses the values to create two Strings, increases the number of views of the page by one, and places all of these values into the database.
There is now only one task left. We have to display the web statistics. Let’s call the following file display.php
:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-US">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Web Statistics Display</title>
</head>
<body>
<?php
# Open up the database
$handle = sqlite_open( $_SERVER['DOCUMENT_ROOT'].'/stats.db', 0666, $sqliteError ) or die( $sqliteError );
# Get all the statistics
$sqlGet = 'SELECT * FROM stats';
$result = sqlite_query( $handle, $sqlGet );
# Create an unordered list
echo "<ul>\n";
# If there are results
if( $result ) {
$page_views = 0;
$unique_visitors = 0;
# Fetch them
while( ($info = sqlite_fetch_array( $result ) ) ) {
# Get the page, views, IPs, and referrers
$page = $info['page'];
$views = $info['views'];
$ips = $info['ip'];
$referrers = $info['referrer'];
# Print out a list element with the $page/$views
information if( $views == 1 ) echo "\t<li>\n\t\t<p>$page was viewed $views time:</p>\n"; else echo "\t<li>\n\t\t<p>$page was viewed $views times:</p>\n"; # Update the number of page views $page_views += $views; # Parse the data of IPs and Referrers using process.php's code preg_match_all( '%(\d+)\((.*?)\)%', $ips, $matches ); # Find the size of the data $size = count( $matches[1] ); # Create a sub list echo "\t\t<ul>\n"; # Loop through all the IPs for( $i = 0; $i < $size; $i++ ) { # Find the number of visits $num = $matches[1][$i]; # Find the IP address $ip = $matches[2][$i]; # Print the info in a list element if( $num == 1 ) echo "\t\t\t<li>$num time by $ip</li>\n"; else echo "\t\t\t<li>$num times by $ip</li>\n"; # Update the number of unique visitors $unique_visitors++; } # Repeat the whole process for referrers preg_match_all( '%(\d+)\((.*?)\)%', $referrers, $matches ); $size = count( $matches[1] ); # Loop through each one for( $i = 0; $i < $size; $i++ ) { $num = $matches[1][$i]; $referrer = $matches[2][$i]; # Print out the info if( $num == 1 ) echo "\t\t\t<li>$num referral by $referrer</li>\n"; else echo "\t\t\t<li>$num referrals by $referrer</li>\n"; } # End the sub-list echo "\t\t</ul>\n"; # End the list element echo "\t</li>\n"; } echo "\t<li>Total unique visitors: $unique_visitors</li>\n"; echo "\t<li>Total page views: $page_views</li>\n"; } # End the unordered list echo "</ul>\n"; # Close the database sqlite_close($handle); ?> </body> </html>
It may seem daunting, but it is very similar to process.php. It parses the page, views, ip addresses, and referrers from the database. It then continues to output these in an unordered list format.
The final output is as follows:
View Demo of your Own Stats Program
Thank you for reading and good luck!
Send Comment:
64 Comments:
More than a year ago
Thanks man
More than a year ago
good work sir
More than a year ago
super!!!
More than a year ago
Useless!!Wasted 5 min of life seeing and commenting
More than a year ago
Thanks for giving some ideas on how I would make a statistics program. I have a question though, would it better to make a flat file based analystics and then have different flat files for different times such as each day.
More than a year ago
The demo is not working anymore, also, build an analytic system based heavily in PHP and mySQL like this can make the server overload.
More than a year ago
how can i applay this on my website? thank you very much
i have tried with google analitics do not work at all
More than a year ago
This was an awesome tutorial! Thank you.
More than a year ago
Very nice! It takes time to post an article like this, and you did a great job! More than anything, it inspires anyone to really tackle the issue.
Such an important issue... All of the web analytics out there throw so much information at the user's face, hopefully something will be developed that will concisely let the user naturally bring her stats up.
Thanks again Karthik!
More than a year ago
The demo link is broken, but the tut is neat. I wanted to see a demo before digging in guess 1/2 a hour worth reading post
More than a year ago
Hi I'm new to php coding and I'm trying to add this to my new website and was wondering what would be a more simple way of just using javascript and not jOuery as you said it would be possible and easier
More than a year ago
wow, nice, I personally uses webalizer and have always wondered how they parse through my log file so fast, but somehow this tutorial made me understand how it works, thanks alot.
and the stats from my site can be seen at webalizer.littlebearz.com :)
More than a year ago
Hey, the demo isn't working!!!
More than a year ago
looks like a nice and easy way to get stats in your site ;)
i would personally not use the #-style comments but use the regular // and /* */ commenting style
More than a year ago
niceee post bro tnx i like your coding
More than a year ago
Google Analytics definitely offers more data such as browsers and OS, and everything is nicely organized but this is definitely a good tutorial. The problem with GAnalytics is that it sometimes slows down pages, even when it's at the bottom of your page.
More than a year ago
This is an excellent tut. And to the rest: This is a tut. Not a copy and paste "script". Where you shut off your brain :)
More than a year ago
This could be useful. Forgiving some of the horrible indenting.
More than a year ago
I added permissions to the root and I ended up with 501 Permission Denied for our website so it didn't do any good.
I see that you made some changes to the code in the process.php file. Nevertheless, I am having the same issue:
If I run the install.php file I get an error message:
Warning: sqlite_open() [function.sqlite-open]: unable to open database: mydomain/stats.db in mydomain/install.php on line 3
unable to open database: mydomain/stats.db
Again, when I create a folder and set permissions for the folder to 777 and point the path to the database in that folder (for ex. '/test/stats.db') on remote server then it is successful and I get "Finished" result. So obviously this is telling me that the database cannot be created directly under root because of permissions. That's why it works under a folder. But when I visit our website or the webpage that I created to track (webpagetrackable.html) and then browse the display.php in browser the output is always:
* Total unique visitors: 0
* Total page views: 0
I also changed permissions on all the files that I created (webpagetrackable.html, install.php, process.php display.php, to 777 just in case, but no difference.
It's odd. If you have any other idea what can be wrong I would appreciate it. If necessary I can send you all the files that I created.
Thanks!!!
More than a year ago
Karthik, I just updated the code in the article.
Thanks for taking the time to write this amazing tutorial to take us to the next level :)
More than a year ago
Thanks for this wicked tips and code samples.
I will definitely find it very useful.
In fact, I will blogroll your blog :)
More than a year ago
Very cool, great tut and I learned a few new things!
More than a year ago
ahh the power of ajax, makes our life of programming applications and custom websites soo much easier, how did we ever live without it
More than a year ago
this is simply great tutorial and i really appreciate your effort.
More than a year ago
That's odd. I don't know the exact modifications you have made to the script, but is there any way you can add permissions to the root? If so, you can test the file as is and see if the same problem is occurring.