<?php

// Title: hitcounter.php

// Author: Burton Rosenberg
// Created: 18 June 2007
// Last Update: 18 June 2007 

// Copyright (c) 2007 Burton Rosenberg. All rights reserved.

require('hitcounter_sub.php') ;

?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>AJAX Hit Counter</title>

<script type="text/javascript" src="hitcounter.js"></script>

<link href="hitcounter.css" rel="stylesheet" type="text/css">
<style type="text/css"><!--
   /* internal styles */
  #debug1 { 
    display: none ;
  }

  #hitcount {
    color: red ;
  }

--></style>

</head>
<body onload="getHitCounterAjax()">

<h1>AJAX Hit Counter</h1>

<div  id="debug1" class="debugbox">
<pre>
<?php
   
// do this here to get debug output

   
incrementHitCounter('mypage') ;
   
$n getHitCounter('mypage') ;

?>
</pre>
</div>

<p>
This page accessed <span id="hitcount"><?=$n?></span> times.
</p>

<h2>
The PHP
</h2>

<p>
The application uses three files:
<ul>
<li>
<a href="hitcounter.phps">hitcounter.php</a>
<li>
<a href="hitcounter_sub.phps">hitcounter_sub.php</a> 
<li>
<a href="hitcounterajax.phps">hitcounterajax.php</a> 
</ul>
The sub file has utility routines, and it is share by the main script and the ajax responder.
Of course, there is a CSS and a Javascript file attached to the main web page.
</p>
<p>
The hitcounter file generates this page, it is the main entry point into the application. The file contains
a great deal of HTML, so I've tried to move all the PHP into a separate file, hitcounter_sub. The Ajax responding
script is in hitcounterajax.
Since the subroutine file is shared between hitcounter and hitcounterajax, and it encapsulates all the true workings
of the application, neither of the other two files is very long.
</p>
<p>
The PHP main script can be set to write its debugging output (echo statements) to the page. Things have been arranged
so that all of that output occurs inside a div which can be marked display none or display block. This way the
debugging will be hidden or visible. 
This <q>poor man's debugging console</q>  can be <a href="javascript:showdebug();">interactively shown or hidden</a> by 
having Javascript set this property.
</p>

<h2>
The Ajax
</h2>

<p>
The getHitCounterAjax javascript routine uses xhttprequest to query the current page count on the server, but calling
the script hitcounterjava.php.
The page is set to poll hitcounterjava every three seconds, at first by the onload event, and then driven by the
completion of the previous xhttprequest.
To make the ajax a little less dependent on the exact format of the return, I use a simple regular expression to capture
an number inclosed between two underscores. 
</p>

<div class="debugbox" style="background: #eeeeff;">
<pre>
function getHitCounterAjax() {
    makeRequest("hitcounterajax.php") ;
}

function processAjaxReply( returnedtext ) {
   // alert(returnedtext) ; // DEBUG (super annoying!)
   var re = /_(\d+)_/ ;
   var n ;
   n = returnedtext.match(re)[1] ;
   document.getElementById('hitcount').textContent = n ;
   // sleep then call getHitCounterAjax again ..
   setTimeout("getHitCounterAjax()", 3000 ) ;
}


// code copied from http://developer.mozilla.org/en/docs/AJAX:Getting_Started

function makeRequest(url) {
   var httpRequest;

   if (window.XMLHttpRequest) { // Mozilla, Safari, ...
      httpRequest = new XMLHttpRequest();
      if (httpRequest.overrideMimeType) {
         httpRequest.overrideMimeType('text/xml');
         // See note below about this line
      }
    }
    else if (window.ActiveXObject) { // IE
       try {
          httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
       }
       catch (e) {
          try {
             httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
          }
          catch (e) {}
       }
   }

   if (!httpRequest) {
       alert('Giving up :( Cannot create an XMLHTTP instance');
       return false;
   }
   httpRequest.onreadystatechange = function() { alertContents(httpRequest); };
   httpRequest.open('GET', url, true);
   httpRequest.send(null);
}

function alertContents(httpRequest) {
   if (httpRequest.readyState == 4) {
      if (httpRequest.status == 200) {
        processAjaxReply( httpRequest.responseText ) ;
        // alert(httpRequest.responseText);
      } else {
         alert('There was a problem with the request.');
      }
   }
}
</pre>
</div>
<h2>
The MySql
</h2>

<p>
Here is the commands for creating the table and granting rights to the PHP user.
</p>


<div class="debugbox" style="background: #eeeeff;">
<pre>

mysql&gt; create database hitcounter ;
Query OK, 1 row affected (0.00 sec)

mysql&gt; use hitcounter ;
Database changed

mysql&gt; create table  hits (
    -&gt; id int primary key not null auto_increment , 
    -&gt; name varchar(12) unique, 
    -&gt; hits int , 
    -&gt; date timestamp
    -&gt; ) ;
Query OK, 0 rows affected (0.08 sec)

mysql&gt; describe hits ;
+-------+-------------+------+-----+-------------------+----------------+
| Field | Type        | Null | Key | Default           | Extra          |
+-------+-------------+------+-----+-------------------+----------------+
| id    | int(11)     |      | PRI | NULL              | auto_increment |
| name  | varchar(12) | YES  | MUL | NULL              |                |
| hits  | int(11)     | YES  |     | NULL              |                |
| date  | timestamp   | YES  |     | CURRENT_TIMESTAMP |                |
+-------+-------------+------+-----+-------------------+----------------+
4 rows in set (0.00 sec)

mysql&gt; insert into hits (name,hits) values ("mypage",0) ;
Query OK, 1 row affected (0.00 sec)

mysql&gt; select * from hits ;
+----+--------+------+---------------------+
| id | name   | hits | date                |
+----+--------+------+---------------------+
|  1 | mypage |    0 | 2007-06-18 11:17:16 |
+----+--------+------+---------------------+
1 row in set (0.00 sec)

mysql&gt; grant select, update on hitcounter.hits to 
    -&gt; myhits@localhost identified by 'xxxxxxxx' ;
Query OK, 0 rows affected (0.00 sec)


</pre>
</div>

</body>
</html>