BEV Hits'n Stats
BEV Home Page,/| Contact,mailto:steve.teale@britseyeview.com| Comment,,#TFC-toggler| Hts'n Stats Demo,/software/hitsnstats/daily.html..The system in action looking at the BEV visits table.| Adia's Place,/ap/| Software,/software/

What Is It

Hits'n Stats is the LAMP application used on this web site to record and report on visits to its pages. It uses geo-data generated by geoplugin.net. Reports are provided for visit details over the last N days, unique daily visits and hits statistics in graphical form for the last N days, the current ranking of your pages, and plain vanilla hit statistics.

It can also act as an old-fashioned hit counter to display hits on particular pages, but I don't know that anyone does that anymore.

I will make a gzip of the files described/used as soon as possible. If you're interested, get in touch - that might make it sooner.

Logging Visits.

The whole thing revolves around a simple PHP script that writes details of each visit to a database table, as follows:

<?php
include "your-db-connect-file.php";
include "browser.php";

function replace_unicode_escape_sequence($match)
{
    return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
}

$ip = $_SERVER['REMOTE_ADDR'];
$path = $_GET["path"];

$a = browser_detect();
$browser = $a[0];
$version = $a[1];


$city = "?";
$region = "?";
$country = "?";
$prefix = "";

if ($ip != "")
{
   $a = unserialize(file_get_contents("http://www.geoplugin.net/php.gp?ip=".$ip));
   $city = $a["geoplugin_city"];
   $city = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $city);
   $region = $a["geoplugin_region"];
   $region = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $region);
   $country = $a["geoplugin_countryName"];
   $country = preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $country);
}

$handle = dbConnect();
if (!$handle) die(sql_error());

$excluded = ($_COOKIE["hs_devmachine"] == "britseye");
$table = "hs_visits";
if ($excluded)
   $table = "hs_devhits";

$t = time();
$tod = gmdate("D, H:i:s", $t);
$dt = gmdate("Ymd", $t);

$sql = "insert into $table values(NULL,$dt,'$tod','$city','$region',
                                  '$country','$ip','$path','$browser', '$version')";

$status = "OK";
if (!mysql_query($sql))
   $status = "Failed";

// We'll return something even if this fails
header("content-type: text/plain");
header("Pragma: no-cache");
header("Expires: 0");

echo("{ \"status\": \"$status\", \"ip\": \"$ip\", \"city\": \"$city\", \"region\": \"$region\", \"country\": \"$country\", \"path\": \"$path\", \"browser\": \"$browser\", \"bversion\": \"$version\" }");
?>

Filtering Development Hits.


The other piece of the jigsaw puzzle is your own hits. If you have a small volume page, or a new page, and you're like me, you will mess with it frequently, and some days half the hits could be you testing things. There are lots of ways to test your page, but the definitive one is to access it. So I want to be able to turn off logging of requests that come from my development machine or machines.

To this end, the BEV Hits'n Stats system can set a cookie on your machine that will prevent hits from you being logged to the main visits table. They go into a separate table with the same structure.

The Database.

There are just two database tables involved. PHP to create the visits table is:

<?php
include "your-db-connect-file.php";

echo("Creating sql\n");
$sql = "vid INT(11) NOT NULL AUTO_INCREMENT,";
$sql .= "vdate INT(11) NOT NULL,";              // 20110612
$sql .= "vtod CHAR(13) NOT NULL,";              // 'Tue, 09:22:32'
$sql .= "city VARCHAR(50),";
$sql .= "region VARCHAR(50),";
$sql .= "country VARCHAR(50),";
$sql .= "ip VARCHAR(20),";
$sql .= "path VARCHAR(200) NOT NULL,";
$sql .= "browser VARCHAR(50) DEFAULT '(unknown)',";
$sql .= "bversion VARCHAR(10) DEFAULT '',";
$sql .= "UNIQUE vid (vid)";

$handle = dbConnect();
if (!$handle) die(sql_error());
echo("Connected to $dbhost<br>\n");

if (!mysql_query("CREATE TABLE hs_visits ($sql)")) die(sql_error());
echo("Created visits table<br>");
if (!mysql_query("CREATE TABLE hs_devhits ($sql)")) die(sql_error());
echo("Created devhits table<br>");
?>
The devhits table is identical to the visits table - that's where hits from your development machine go if the cookie is set. If you're an SQL/MySQL aficionado you can suggest some indexes to make this stuff run quicker ;=) Reports.

The reports provided by the base Hits'n Stats system are:

Scripting a Page with Visit Logging

All that needs to be done to record a visit is a jQuery get to hs_logvisit.php, recording the result in a jQuery's data object, and a user defined function to do somthing with the result. This can be incorporated into a page as follows:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<head>
<link rel="stylesheet" href="/common/bev.css" type="text/css" />
<script src="/script/jquery.js" type="text/javascript"></script>
<script>
// This is a user provided function to do whatever is required with
// the results from hs_logvisit.php
function onLogVisit(v)
{
   v = $.parseJSON(v);
   // v is an object representing what hs_logvisit.php sent back
   $('#city').html(v.city);
   $('#region').html(v.region);
   $('#country').html(v.country);
   var browser = v.browser+" "+v.bversion;
   $('#browser').html(browser);
   $('#path').html(v.path);   
}

(function($) {
$.logvisit = function()
   {
      var url = "/php/hs_logvisit.php?path="+document.location.pathname;
      $.get(url, function(data) { onLogVisit(data); });
   }
})( jQuery );

// This is where your other jQuery initializations go too
$(function() {
   $.logvisit()
});
</script>
</head>
<body class="bevstd">
<div style="position:relative; width: 802px; margin: 0 auto; text-align: left;">
<h2>BEV Hits'n Stats - logvisit test</h2>
Geo-Data from logvisit was: <span id="city"></span>, <span id="region"></span>, <span id="country"></span>. 
<p />
Your browser is: <span id="browser"></span>
<p />
This page is: <span id="path"></span>
</body>
</html>

Reports.


The reports provided by the base Hits'n Stats system are: The reports are based on data returned by web services, so you can use the ones provided, or roll your own based on the example code.

It's possible to run the report web pages from the local server on your development machine.

Daily Stats Report.

The web service to provide the information for this report is as follows:

<?php
include "your-db-connect-file.php";

$handle = dbConnect();
if (!$handle) die(sql_error());

$path = $_GET["path"];
$days = $_GET["days"];
$mode = $_GET["mode"];
if (!$mode)
   $mode = "script";

$now = time();
$tomorrow = gmdate("Ymd", $now+24*3600);
$before = gmdate("Ymd", $now-($days-1)*24*3600);
$grows = array(); // rows of the chart

for ($i = 0; $i < $days; $i++)
{
   $grows[$i] = array("df"=>"", "dc"=>"", "ic"=>"0/0", "vc"=> 0, "hc"=> 0);
   $grows[$i]["df"] = gmdate("Ymd", $now);  // 20110619 as in database
   $grows[$i]["dc"] = gmdate("D, d/m", $now);
   $now -= 24*3600;
}
$grows[$days] = array(0 => 0, 1 => 0, 2 => 0);

$query = "select vdate, COUNT(DISTINCT ip) as visitors, COUNT(*) as hits from hs_visits where ";
if ($path)
   $query .= "path like '$path%' and ";
$query .= "vdate >= $before and vdate < $tomorrow GROUP BY vdate order by vdate desc";

$result = mysql_query($query);
if (!$result) die(sql_error()." ".$query);
$n = mysql_num_rows($result);

$maxhits = 0;
$rvis = 0;
$rhits = 0;

$i = 0;
while ($row = mysql_fetch_row($result))
{
   $dn = $row[0];
   while ($dn != $grows[$i]["df"])
   {
      $i++;
   }
   $v = (int) $row[1];
   $h = (int) $row[2];
   $rvis += $v;
   $rhits += $h;
   $grows[$i]["ic"] = "$v/$h";
   $grows[$i]["vc"] = $v;
   $grows[$i]["hc"] = $h;
   if ($h > $maxhits)
      $maxhits = $h;
   $i++;
}

$rows = $i;
$mult = $maxhits? 500/$maxhits: 1;
$grows[$days][0] = $maxhits;
$grows[$days][1] = $rvis;
$grows[$days][2] = $rhits;
$json = json_encode($grows);

header("Pragma: no-cache");
header("Expires: 0");
if ($mode == "script")
{
   header("content-type: text/javascript");
   echo("var hits_report_data = $json;");
}
else if ($mode == "json")
{
   header("content-type: application/json");
   echo($json);
}
else
{
   header("content-type: text/plain");
   echo($json);
}
echo($json);
?>
It should be fairly clear how this works. The kernel of it is the SQL query with a count of distinct ip addresses within grouped records matching the date, and a count of the number of items in each group.

You can pass a '...php?path=/software/' parameter to this script to limit the report to pages prefixed by that path.

A set of arrays corresponding to the required days is pre-populated with no-hit values, then updated after the database query. Running totals are summed up as this happens.

You'll notice that this script can produce three different results, while I'm nominally creating JSON from the array of arrays, the function can actually return something that jQuery will accept as JSON, a Javascript snippet like:


var hits_report_data = [{...},{...}];
or plain text. This Javascript snippet allows the page to be on localhost, but to pull the data from your remote server - poor man's cross site stuff.

Once you've got that up and running against the database on the remote server, you can create a local web page to view the result:


<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="/common/bev.css" type="text/css" />
<style>
.gcontainer
    {
       width:800px;
       border:solid 1px black;
       padding:10px;
    }
.dc { 
      float:left; 
      width:7em; 
      height:15px;
      padding-top:2px;
    }
.ic {
      float:left;
      width:6em;
      height:15px;
      padding:2px 3px 0px 0px;
    }
.vc { 
      float:left;
      height:15px;
      background-color:red;
      border: solid 1px black;
    }
.hc { 
      float:left;
      height:15px;
      background-color:yellow;
      border-top: solid 1px black;
      border-right: solid 1px black;
      border-bottom: solid 1px black;
    }
.pad {
      display:block;
      width:10px;
      height:2px;
      margin: 0px;
      padding: 0px;
      clear:both;
    }
</style>
<script src="/script/jquery.js" type="text/javascript"></script>
<script src="http://britseyeview.com/php/hs_daily.php?days=30" type="text/javascript"></script>
<script>
function drawChart()
{
   var today = new Date();
   var m = today.getMonth()+1;
   if (m < 10) m = "0"+m;
   var d = today.getDate();
   if (d < 10) d = "0"+d;
   var n = hits_report_data.length-1;
   $('#vhhead').html("Visits and Hits - Last "+n+" days before and including "+today.getFullYear()+"/"+
                             m+"/"+today.getDate());
   var ctr = $('#chart');
   var maxh = hits_report_data[hits_report_data.length-1][0];
   var tv = hits_report_data[hits_report_data.length-1][1];
   var th = hits_report_data[hits_report_data.length-1][2];
   var adj = (maxh < 600)? 600/maxh: maxh/600;
   var t, v, h, i;
   var i = 0;
   for (i = 0; i < hits_report_data.length-1; i++)
   {
      t = $('<div class="dc">'+hits_report_data[i].dc+'</div>');
      ctr.append(t);
      t = $('<div class="ic">('+hits_report_data[i].ic+')</div>');
      ctr.append(t);
      v = Math.floor(hits_report_data[i].vc*adj);
      h = Math.floor((hits_report_data[i].hc-hits_report_data[i].vc)*adj);
      t = $('<div class="vc" style="width:'+v+'px">');
      ctr.append(t);
      if (h)
      {
         t = $('<div class="hc" style="width:'+h+'px">');
         ctr.append(t);
      }
      t = $('<div class="pad"></div>');
      ctr.append(t);
   }
   i++
   t = $('<div class="pad" style="height:4px;"></div>');
   ctr.append(t);
   t = $('<div class="dc">'+n+' day avg.:</div>');
   ctr.append(t);
   v = tv/n;
   h = th/n;
   var ah = Math.floor(adj*(th/n));
   h -= v;
   v = Math.floor(v*adj);
   h = Math.floor(h*adj);
   t = $('<div class="ic">('+v+'/'+ah+')</div>');
   ctr.append(t);
   t = $('<div class="vc" style="width:'+v+'px">');
   ctr.append(t);
   if (h)
   {
      t = $('<div class="hc" style="width:'+h+'px">');
      ctr.append(t);
   }
   t = $('<div class="pad"></div>');
   ctr.append(t);
   
   $('#summary').html("Visitors "+tv+", Hits "+th);
}

$(function() {
   drawChart();
   setTimeout('window.location.reload()', 1000*5*60);
});
</script>
</head>
<body class="bevstd">
<div style="position:relative; width: 802px; margin: 0 auto; text-align: left;">
<h2 id="vhhead">Visits and Hits - Last N Days</h2>
<div id="chart" class="gcontainer">
</div>
   <p class="pad10" />
<div style="font-size:11pt">Totals for period: <span id="summary"></span>.</div>
</div>
</body>
</html>
I've left the styles in the html page so you can see what's going on. The bar chart is just drawn using left floated divs with different background colors. Not a table in sight ;=)

You can see the result here.

Recent Activity Report

This is just a simple date-limited query on the visits table, with the results filled into a minimally styled table that can stretch in width to accommodate longish paths. The PHP for the lookup is:

<?php
include "your-db-connect-file.php";

function replace_unicode_escape_sequence($match)
{
   return mb_convert_encoding(pack('H*', $match[1]), 'UTF-8', 'UCS-2BE');
}

function xlate($s)
{
   $s = preg_replace_callback('/u([0-9a-f]{4})/i', 'replace_unicode_escape_sequence', $s);
	return $s;
}

$days = (int) $_GET["days"];
if (!$days)
   $days = 3;
$mode = $_GET["mode"];
if (!$mode)
   $mode="script";

$handle = dbConnect();
if (!$handle) die(sql_error());

$tomorrow = gmdate("Ymd", time()+24*3600);
$before = gmdate("Ymd", time()-$days*24*3600);
$sql = "select vdate,vtod,city,region,country,ip,path from hs_visits where vdate > $before and vdate < $tomorrow order by vid desc";


$result = mysql_query($sql);
if (!$result) die(sql_error());

$rrows = array();
$i = 0;
while ($row = mysql_fetch_row($result))
{
   $rrows[$i] = array("dt" => "", "city" => "", "region" => "", "country" => "", "ip" => "", "path" => "");
   $rrows[$i]["dt"] = $row[1];

   // The geo data has Unicode escapes in it
   $rrows[$i]["city"] = xlate($row[2]);
   $rrows[$i]["region"] = xlate($row[3]);
   $rrows[$i]["country"] = xlate($row[4]);
   $rrows[$i]["ip"] = $row[5];
   $rrows[$i]["path"] = $row[6];
   $i++;
}
$json = json_encode($rrows);

header("Pragma: no-cache");
header("Expires: 0");
if ($mode == "script")
{
   header("content-type: text/javascript");
   echo("var recent_report_data = $json;");
}
else if ($mode == "json")
{
   header("content-type: application/json");
   echo($json);
}
else
{
   header("content-type: text/plain");
   echo($json);
}
?>
The HTML is then as follows (the download has an example of the code when you place the report HTML files on your local machine).

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="/common/bev.css" type="text/css" />
<style>
thead { background-color: #eeeeee; padding:0px 5px 0px 5px;}
</style>
<script src="/script/jquery.js" type="text/javascript"></script>
<script>
function fillTable(rd)
{
   var d = new Date();
   var m = d.getMonth()+1;
   if (m < 10)
      m = "0"+m;
   var day = d.getDate();
   if (day < 10)
      day = "0"+day;
   var ds = ""+d.getFullYear()+"/"+m+"/"+day;
   $('#today').html(ds);
   var tb = $('#tablebody');
   for (var i = 0; i < rd.length; i++)
   {
      var s = '<tr style="border:solid 1px black;"><td>'+rd[i].dt+'</td>';
      s += "<td>"+rd[i].city+"</td>";
      s += "<td>"+rd[i].region+"</td>";
      s += "<td>"+rd[i].country+"</td>";
      s += "<td>"+rd[i].ip+"</td>";
      s += "<td>"+rd[i].path+"</td></tr>";
      var row = $(s);
      tb.append(row);
   }
}

(function($) {
$.getHitsReport = function()
   {
      var url = "/php/hs_recent.php?days=3&mode=json";
      $.get(url, function(rd) { fillTable(rd); });
   }
})( jQuery );

$(function() {
   $.getHitsReport();
   setTimeout('window.location.reload()', 1000*5*60);
});
</script>
</head>
<body class="bevstd">
<div style="position:relative; width: 800px; margin: 0 auto; text-align: left;">
<h2 id="vhhead">Recent Hits - 3 days including today <span id="today"></span>.</h2>
</div>
<table style="border:solid 1px black; padding:5px; margin:0 auto;">
<thead>
<tr><th>Date/Time</th><th>City</th><th>Region</th><th>Country</th><th>IP</th><th>Path</th></tr>
</thead>
<tbody id="tablebody" style="padding:5px;">
</tbody>
</table>
</body>
</html>
You can see the result here.

Most Popular Pages.

You know the general form of this from above - PHP:

<?php
include "your-db-connect-file.php";

$mode = $_GET["mode"];
if (!$mode)
   $mode="script";

$handle = dbConnect();
if (!$handle) die(sql_error());

$query = "select path, COUNT(DISTINCT ip) as pvisits, COUNT(*) as phits FROM hs_visits ";
$query .= "GROUP BY path order by phits DESC LIMIT 20";

$result = mysql_query($query);
if (!$result) die(sql_error()." ".$query);
$top20 = array();
$rtv = 0;
$rth = 0;

$i = 0;
for ($i = 0; $row = mysql_fetch_row($result); $i++)
{
   $top20[$i] = array("path" => "", "vc" => "", "hc" => "");
	$top20[$i]["path"] = $row[0];
	$v = (int) $row[1];
	$rtv += $v;
	$top20[$i]["vc"] = $v;
	$h = (int) $row[2];
	$rth += $h;
	$top20[$i]["hc"] = $h;
}

$query = "select COUNT(DISTINCT ip) as pvisits, COUNT(*) as phits FROM hs_visits";
$result = mysql_query($query);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$top20[$i] = array("path" => "", "vc" => 0, "hc" => 0);
$top20[$i]["vc"] = $row[0];
$top20[$i]["hc"] = $row[1];
$json = json_encode($top20);

header("Pragma: no-cache");
header("Expires: 0");
if ($mode == "script")
{
   header("content-type: text/javascript");
   echo("var top20_report_data = $json;");
}
else if ($mode == "json")
{
   header("content-type: application/json");
   echo($json);
}
else
{
   header("content-type: text/plain");
   echo($json);
}
?>
HTML:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="/common/bev.css" type="text/css" />
<style>
.gcontainer
    {
       width:400px;
       border:solid 1px black;
       padding:10px;
    }
.path { 
      float:left; 
      width:18em; 
      height:15px;
      padding-top:2px;
    }
.nc { 
      float:left;
      width:6em;
      height:15px;
      padding-top:2px;
      text-align:right;
    }
.pad {
      display:block;
      width:10px;
      height:2px;
      margin: 0px;
      padding: 0px;
      clear:both;
    }
</style>
<script src="/script/jquery.js" type="text/javascript"></script>
<script>
function listTop20(t20)
{
   var today = new Date();
   var m = today.getMonth()+1;
   if (m < 10) m = "0"+m;
   var d = today.getDate();
   if (d < 10) d = "0"+d;
   var n = t20.length-1;
   $('#vhhead').html("Top 20 Pages - as at "+today.getFullYear()+"/"+
                             m+"/"+today.getDate());
   var ctr = $('#chart');
   var tv = t20[t20.length-1]["vc"];
   var th = t20[t20.length-1]["hc"];
   var t, v, h, i;
   var i = 0;
   for (i = 0; i < t20.length-1; i++)
   {
      t = $('<div class="path">'+t20[i].path+'</div>');
      ctr.append(t);
      t = $('<div class="nc">'+t20[i].vc+'</div>');
      ctr.append(t);
      t = $('<div class="nc">'+t20[i].hc+'</div>');
      ctr.append(t);
      t = $('<div class="pad"></div>');
      ctr.append(t);
   }
   i++
   t = $('<div class="pad" style="height:4px;"></div>');
   ctr.append(t);
   t = $('<div class="path">Totals:</div>');
   ctr.append(t);
   t = $('<div class="nc">'+tv+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+th+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);
}

(function($) {
$.getTop20Report = function()
   {
      var url = "/php/hs_top20.php?mode=json";
      $.get(url, function(t20) { listTop20(t20); });
   }
})( jQuery );

$(function() {
   $.getTop20Report();
   setTimeout('window.location.reload()', 1000*5*60);
});
</script>
</head>
<body class="bevstd">
<div style="position:relative; width: 802px; margin: 0 auto; text-align: left;">
<h2 id="vhhead"></h2>
<div id="chart" class="gcontainer">
</div>
</body>
</html>
The result is here.

Hit Counting.

The old fashioned hit counters used to provide you with an image of the current count in some horrid selection of rather gaudy fonts. The BEV hit counter (not used on the site except for the demo page) simply provides a simple Javascript object via JSON, so you can display the information in some element of your page using jQuery, or as you please.

The object provides information on visits and hits, for today, the last 7 days, this month, this year, and for the entirety of your visits table. So, just once more, the PHP:


<?php
include "your-db-connect-file.php";

$mode = $_GET["mode"];
if (!$mode)
   $mode="script";

$handle = dbConnect();
if (!$handle) die(sql_error());

$path = $_GET["path"];
$ident = $_GET["ident"];

$now = time();
$tomorrow = gmdate("Ymd", $now+24*3600);
$yesterday = gmdate("Ymd", $now-24*3600);
$weekago = gmdate("Ymd", $now-7*24*3600);
$t = (int) gmdate("j");
$thismonth = gmdate("Ymd", $now-$t*24*3600);
$t = (int) gmdate("Y", $now);
$t--;
$td = date_create("$t-12-31");
$prevyearend = $td->format("Ymd");
$ever = "19900101";

$stats = array(); // rows of the chart

$query = "select COUNT(DISTINCT ip) as visitors, COUNT(*) as hits from visits where ";
if ($path)
	$query .= "path like '$path%' and ";
$query .= "vdate >= @@@@ and vdate < $tomorrow";

$sql = str_replace("@@@@", $yesterday, $query);
$result = mysql_query($sql);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$stats["day"] = array(0 => $row[0], 1 => $row[1]);

$sql = str_replace("@@@@", $weekago, $query);
$result = mysql_query($sql);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$stats["week"] = array(0 => $row[0], 1 => $row[1]);

$sql = str_replace("@@@@", $thismonth, $query);
$result = mysql_query($sql);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$stats["month"] = array(0 => $row[0], 1 => $row[1]);

$sql = str_replace("@@@@", $prevyearend, $query);
$result = mysql_query($sql);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$stats["year"] = array(0 => $row[0], 1 => $row[1]);

$sql = str_replace("@@@@", $ever, $query);
$result = mysql_query($sql);
if (!$result) die(sql_error()." ".$query);
$row = mysql_fetch_row($result);
$stats["ever"] = array(0 => $row[0], 1 => $row[1]);

$json = json_encode($stats);
header("Pragma: no-cache");
header("Expires: 0");
if ($mode == "script")
{
   header("content-type: text/javascript");
   echo("var hits_stats_data$ident = $json;");
}
else if ($mode == "json")
{
   header("content-type: application/json");
   echo($json);
}
else
{
   header("content-type: text/plain");
   echo($json);
}
?>
There is one slightly different point here. You can invoke this with a parameter '...php?ident=1' or whatever. The if you are using the cross site approach, you can do multiple script includes to get stats for different paths on the same page. If you're using JSON you can just make as many AJAX calls as you need.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<link rel="stylesheet" href="/common/bev.css" type="text/css" />
<style>
.gcontainer
    {
       width:400px;
       border:solid 1px black;
       padding:10px;
    }
.period { 
      float:left; 
      width:12em; 
      height:15px;
      padding-top:2px;
    }
.nc { 
      float:left;
      width:6em;
      height:15px;
      padding-top:2px;
      text-align:right;
    }
.pad {
      display:block;
      width:10px;
      height:2px;
      margin: 0px;
      padding: 0px;
      clear:both;
    }
</style>
<script src="/script/jquery.js" type="text/javascript"></script>
<script>
function listStats(stats)
{
   var today = new Date();
   var m = today.getMonth()+1;
   if (m < 10) m = "0"+m;
   var d = today.getDate();
   if (d < 10) d = "0"+d;
   var n = stats.length-1;
   $('#vhhead').html("Hits statistics - as at "+today.getFullYear()+"/"+
                             m+"/"+today.getDate());
   var ctr = $('#stats');
   var t = $('<div class="period">Today</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.day[0]+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.day[1]+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);

   t = $('<div class="period">This Week (last 7 days)</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.week[0]+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.week[1]+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);

   t = $('<div class="period">This Month</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.month[0]+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.month[1]+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);

   t = $('<div class="period">This Year</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.year[0]+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.year[1]+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);

   t = $('<div class="period">Entire Database</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.ever[0]+'</div>');
   ctr.append(t);
   t = $('<div class="nc">'+stats.ever[1]+'</div>');
   ctr.append(t);
   t = $('<div class="pad"></div>');
   ctr.append(t);
}

(function($) {
$.getStats = function()
   {
      var url = "/php/hs_stats.php?mode=json";
      $.get(url, function(stats) { listStats(stats); });
   }
})( jQuery );

$(function() {
   $.getStats();
   setTimeout('window.location.reload()', 1000*5*60);
});
</script>
</head>
<body class="bevstd">
<div style="position:relative; width: 802px; margin: 0 auto; text-align: left;">
<h2 id="vhhead"></h2>
<div id="stats" class="gcontainer">
</div>
</body>
</html>
And the demo here.