PHP visitor tracking script with jQuery and Raphael javascript library

This simple PHP visitor tracking script uses jQuery and Raphael javascript library to display daily hits to your page. The class features:

  • IP filter (for IP’s you don’t want to track)
  • Unique visits counter
  • Raphael analytics (example on the bottom of the page)
  • No search engine robots tracking
  • Total hits counter
  • Visitor IP’s info
  • Visitor host info
  • Referring pages info
  • Visited pages info
  • MySQL log table creation

Dependencies

MySQL Table

This table could be expanded as needed. For example, you could add “browser” field to collect visitor browser information.

CREATE TABLE IF NOT EXISTS logger (
              	logid INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
				ip VARCHAR( 16 ) NOT NULL ,
				host VARCHAR( 100 ) NOT NULL ,
				visitdate DATETIME NOT NULL ,
				visitedpage VARCHAR(255) NOT NULL ,
				referring VARCHAR( 255 ) NOT NULL
            )

PHP Class (class.logger.php)

IMPORTANT: Make sure to fill in the database info. See comments for the details.

<?php
//start session
class logger {
//database setup
var $hostname_logon = 'localhost';  //Database server LOCATION
var $database_logon = '';    //Database NAME
var $username_logon = '';    //Database USERNAME
var $password_logon = '';    //Database PASSWORD

//table fields
var $tablename = '';        //logger table name
var $filter = array();      //array of ip's you don't want tracked

//connect to database
function dbconnect(){
    $connections = mysql_connect($this->hostname_logon, $this->username_logon, $this->password_logon) or die ('Unabale to connect to the database');
    mysql_select_db($this->database_logon) or die ("Error in query: $qry. " . mysql_error());
}

//log visit
function logvisit($ip, $host, $filename, $referring){
    //check if ip is filtered
    if(!in_array($ip,$this->filter)){
        $qry = "INSERT INTO ".$this->tablename." (ip, host, visitdate, visitedpage, referring)VALUES('".$ip."','".$host."',NOW(), '".$filename."', '".$referring."' )";
        $result = mysql_query($qry) or die ("Error in query: $qry. " . mysql_error());

    }
}

//show total uniqe visitors to date(parameter: (int) number of months). For example: 3 will start counting from 3 monhts before today.
function totalunique($month){
    $lastmonth = mktime(01, 01, 01, date("m")-$month, date("d"),   date("Y"));
    $from =  date("Y-m-d H:i:s", $lastmonth);
    $to = date("Y-m-d H:i:s");
    $qry = "SELECT DISTINCT ip, visitdate, host FROM ".$this->tablename." WHERE visitdate BETWEEN '".$from."' AND '".$to."' GROUP BY ip";
    $result = mysql_query($qry) or die ("Error in query: $qry. " . mysql_error());
    $totalunique = mysql_num_rows($result);
    return $totalunique;
}

//show total visits to date(parameter: (int) number of months). For example: 3 will start counting from 3 monhts before today.
function totalhits($month){
    $lastmonth = mktime(01, 01, 01, date("m")-$month, date("d"),   date("Y"));
    $from =  date("Y-m-d H:i:s", $lastmonth);
    $to = date("Y-m-d H:i:s");
    $qry = "SELECT ip, visitdate, host FROM ".$this->tablename." WHERE visitdate BETWEEN '".$from."' AND '".$to."'";
    $result = mysql_query($qry) or die ("Error in query: $qry. " . mysql_error());
    $totalhitsmonth = mysql_num_rows($result);
    return $totalhitsmonth;
}

//show daily visits for a specific month /  year and div in which to laod the graph
function dailyhits($month, $year, $div, $tableid){
    //current month and year
    if($month == ""){
        $month = date("m");
    }
    if($year == ""){
        $year = date("Y");
    }

    //get number of days
    $numdaysinmonth = cal_days_in_month(CAL_GREGORIAN, $month, $year);          

    echo '<table cellpadding="0" cellspacing="0" border="0" class="'.$tableid.'" >
    <tfoot>
        <tr>';

        for($x=1;$x<=$numdaysinmonth;$x++){
            echo '<th>'.$x.'</th>';
        }

    echo '</tr>
    </tfoot>
    <tbody>
        <tr>';
        $totalhits = 0;
        for($y=1;$y<=$numdaysinmonth;$y++){
         if($y < 10){
            $b = '0'.$y;
         }else{
            $b = $y;
         }
         $current_month = date('Y-m').'-'.$b;
         $qry = "SELECT DISTINCT ip FROM ".$this->tablename." WHERE visitdate LIKE '$current_month%' GROUP BY visitdate";
         $result = mysql_query($qry) or die ("Error in query: $qry. " . mysql_error());
         $numrows = mysql_num_rows($result);

            echo '<td>';
            echo $numrows;
            $totalhits = $totalhits + $numrows;
            echo '</td>';
        }

     echo '</tr>
    </tbody>
    </table>
    <div id="'.$div.'"></div>';
}

//display visit details (parameter (int) number of last visits to display
function visitdetails($num){

    $qry = "SELECT * FROM ".$this->tablename." WHERE visitdate!='' ORDER BY logid DESC LIMIT 0, ".$num."";
    $result = mysql_query($qry) or die ("Error in query: $qry. " . mysql_error());;

    if (mysql_num_rows($result) != 0){
        echo '<table cellpadding="0" cellspacing="0" border="0" class="visitdetails">';
        echo '<thead><tr><th>IP</th><th>Host</th><th>Referring Page</th><th>Page Visited</th><tr></thead>';
        echo '<tbody>';
        while ($row = mysql_fetch_object($result)){
            $nowww = ereg_replace('www.','',$row->referring);
            $domain = parse_url($nowww);
            if(!empty($domain["host"]))  {
                $host = $domain["host"];
            }
          echo '<tr><td>'.$row->ip.'</td>
          <td>'.$row->host.'</td>
          <td><a href="'.$row->referring.'" target="_blank">'.$host.'</a></td>
          <td><a href="..'.$row->filename.'" target="_blank">'.$row->filename.'</a></td>
          </tr>';
        }
        echo '</tbody>';
        echo '</table>';
    }
}

//raphael stuff(depends on including raphael.js library
function loadraphael($tableid, $div, $month_year, $width, $height, $leftgutter, $bottomgutter, $topgutter,$strokecolor, $fontcolor,$fillcolor,$gridcolor,$raphael_path){
//load latest jquery. Uncomment if you are already using jquery.
echo '<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>';
echo '<script type="text/javascript" src="'.$raphael_path.'"></script><script type="text/javascript">
    //raphael.path.methods.js
    Raphael.el.isAbsolute = true;
    Raphael.el.absolutely = function () {
        this.isAbsolute = 1;
        return this;
    };
    Raphael.el.relatively = function () {
        this.isAbsolute = 0;
        return this;
    };
    Raphael.el.moveTo = function (x, y) {
        this._last = {x: x, y: y};
        return this.attr({path: this.attrs.path + ["m", "M"][+this.isAbsolute] + parseFloat(x) + " " + parseFloat(y)});
    };
    Raphael.el.lineTo = function (x, y) {
        this._last = {x: x, y: y};
        return this.attr({path: this.attrs.path + ["l", "L"][+this.isAbsolute] + parseFloat(x) + " " + parseFloat(y)});
    };
    Raphael.el.arcTo = function (rx, ry, large_arc_flag, sweep_flag, x, y, angle) {
        this._last = {x: x, y: y};
        return this.attr({path: this.attrs.path + ["a", "A"][+this.isAbsolute] + [parseFloat(rx), parseFloat(ry), +angle, large_arc_flag, sweep_flag, parseFloat(x), parseFloat(y)].join(" ")});
    };
    Raphael.el.curveTo = function () {
        var args = Array.prototype.splice.call(arguments, 0, arguments.length),
            d = [0, 0, 0, 0, "s", 0, "c"][args.length] || "";
        this.isAbsolute && (d = d.toUpperCase());
        this._last = {x: args[args.length - 2], y: args[args.length - 1]};
        return this.attr({path: this.attrs.path + d + args});
    };
    Raphael.el.cplineTo = function (x, y, w) {
        this.attr({path: this.attrs.path + ["C", this._last.x + w, this._last.y, x - w, y, x, y]});
        this._last = {x: x, y: y};
        return this;
    };
    Raphael.el.qcurveTo = function () {
        var d = [0, 1, "t", 3, "q"][arguments.length],
            args = Array.prototype.splice.call(arguments, 0, arguments.length);
        if (this.isAbsolute) {
            d = d.toUpperCase();
        }
        this._last = {x: args[args.length - 2], y: args[args.length - 1]};
        return this.attr({path: this.attrs.path + d + args});
    };
    Raphael.el.addRoundedCorner = function (r, dir) {
        var rollback = this.isAbsolute;
        rollback && this.relatively();
        this._last = {x: r * (!!(dir.indexOf("r") + 1) * 2 - 1), y: r * (!!(dir.indexOf("d") + 1) * 2 - 1)};
        this.arcTo(r, r, 0, {"lu": 1, "rd": 1, "ur": 1, "dl": 1}[dir] || 0, this._last.x, this._last.y);
        rollback && this.absolutely();
        return this;
    };
    Raphael.el.andClose = function () {
        return this.attr({path: this.attrs.path + "z"});
    };

    //raphael analytics
    Raphael.fn.drawGrid = function (x, y, w, h, wv, hv, color) {
    color = color || "'.$fillcolor.'";
    var path = ["M", x, y, "L", x + w, y, x + w, y + h, x, y + h, x, y],
        rowHeight = h / hv,
        columnWidth = w / wv;
    for (var i = 1; i < hv; i++) {
        path = path.concat(["M", x, y + i * rowHeight, "L", x + w, y + i * rowHeight]);
    }
    for (var i = 1; i < wv; i++) {
        path = path.concat(["M", x + i * columnWidth, y, "L", x + i * columnWidth, y + h]);
    }
    return this.path(path.join(",")).attr({stroke: color});
    };
    $(function () {
        $(".'.$tableid.'").css({
            position: "absolute",
            left: "-9999em",
            top: "-9999em"
        });
    });
    window.onload = function () {
    // Grab the data
    var labels = [],
        data = [];
    $(".'.$tableid.' tfoot th").each(function () {
        labels.push($(this).html());
    });
    $(".'.$tableid.' tbody td").each(function () {
        data.push($(this).html());
    });

    // Draw
    var width = '.$width.',
        height = '.$height.',
        leftgutter = '.$leftgutter.',
        bottomgutter = '.$bottomgutter.',
        topgutter = '.$topgutter.',
        colorhue = .6 || Math.random(),
        color = "hsb(" + [colorhue, 1, .75] + ")",
        r = Raphael("'.$div.'", width, height),
        txt = {font: \'12px Fontin-Sans, Arial\', fill: "'.$fontcolor.'"},
        txt1 = {font: \'10px Fontin-Sans, Arial\', fill: "'.$fontcolor.'"},
        txt2 = {font: \'12px Fontin-Sans, Arial\', fill: "'.$fillcolor.'"},
        X = (width - leftgutter) / labels.length,
        max = Math.max.apply(Math, data),
        Y = (height - bottomgutter - topgutter) / max;
    r.drawGrid(leftgutter + X * .5, topgutter, width - leftgutter - X, height - topgutter - bottomgutter, 10, 10, "'.$gridcolor.'");
    var path = r.path().attr({stroke: color, "stroke-width": 4, "stroke-linejoin": "round"}),
        bgp = r.path().attr({stroke: "none", opacity: .3, fill: color}).moveTo(leftgutter + X * .5, height - bottomgutter),
        frame = r.rect(10, 10, 100, 40, 5).attr({fill: "'.$fillcolor.'", stroke: "'.$strokecolor.'", "stroke-width": 2}).hide(),
        label = [],
        is_label_visible = false,
        leave_timer,
        blanket = r.set();
    label[0] = r.text(60, 10, "24 hits").attr(txt).hide();
    label[1] = r.text(60, 40, "22 September 2008").attr(txt1).attr({fill: color}).hide();
    for (var i = 0, ii = labels.length; i < ii; i++) {
        var y = Math.round(height - bottomgutter - Y * data[i]),
            x = Math.round(leftgutter + X * (i + .5)),
            t = r.text(x, height - 6, labels[i]).attr(txt).toBack();
        bgp[i == 0 ? "lineTo" : "cplineTo"](x, y, 10);
        path[i == 0 ? "moveTo" : "cplineTo"](x, y, 10);
        var dot = r.circle(x, y, 5).attr({fill: color, stroke: "'.$fillcolor.'"});
        blanket.push(r.rect(leftgutter + X * i, 0, X, height - bottomgutter).attr({stroke: "none", fill: "'.$fontcolor.'", opacity: 0}));
        var rect = blanket[blanket.length - 1];
        (function (x, y, data, lbl, dot) {
            var timer, i = 0;
            $(rect.node).hover(function () {
                clearTimeout(leave_timer);
                var newcoord = {x: +x + 7.5, y: y - 19};
                if (newcoord.x + 100 > width) {
                    newcoord.x -= 114;
                }
                frame.show().animate({x: newcoord.x, y: newcoord.y}, 200 * is_label_visible);
                label[0].attr({text: data + " hit" + ((data % 10 == 1) ? "" : "s")}).show().animateWith(frame, {x: +newcoord.x + 50, y: +newcoord.y + 12}, 200 * is_label_visible);
                label[1].attr({text: lbl + " '.$month_year.'"}).show().animateWith(frame, {x: +newcoord.x + 50, y: +newcoord.y + 27}, 200 * is_label_visible);
                dot.attr("r", 7);
                is_label_visible = true;
            }, function () {
                dot.attr("r", 5);
                leave_timer = setTimeout(function () {
                    frame.hide();
                    label[0].hide();
                    label[1].hide();
                    is_label_visible = false;
                    // r.safari();
                }, 1);
            });
        })(x, y, data[i], labels[i], dot);
    }
    bgp.lineTo(x, height - bottomgutter).andClose();
    frame.toFront();
    label[0].toFront();
    label[1].toFront();
    blanket.toFront();
    };</script>';
}

    //function to create Logger table
    function createtable($tablename){
	    $qry = "CREATE TABLE IF NOT EXISTS ".$tablename." (
              	logid INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
				ip VARCHAR( 16 ) NOT NULL ,
				host VARCHAR( 100 ) NOT NULL ,
				visitdate DATETIME NOT NULL ,
				visitedpage VARCHAR(255) NOT NULL ,
				referring VARCHAR( 255 ) NOT NULL
            )";
        $result = mysql_query($qry) or die(mysql_error());
        return;
    }  

}
?>

Usage

Make sure to include and instantiate the class on every page you use it.

include("class.logger.php");
$log = new logger();                  //Instentiate the class
$log->tablename = "logger";       //set logger table name
$log->dbconnect();                    //connect to the database

Create Logger Table

Run this code only once to create the log on table.

include("class.logger.php");                   //path to class.logger.php file
$log = new logger();                            //Instentiate the class
$log->dbconnect();                             //connect to database
//pass the name of the table as the parameter. I.e. 'logger"
$log->cratetable('logger');

Log visits

Create a file logger.php and place it inside a folder “/logger“.

<?php
header('Content-type: text/javascript');
include("class.logger.php");                   //path to class.logger.php file
$log = new logger();                            //Instentiate the class
$log->tablename = "logger";                //set logger table name
$log->dbconnect();                             //connect to database
//$log->filter = array('00.00.00.000');      //array of IP addresses you don't want to track

//get IP
$ip = $_SERVER['REMOTE_ADDR'];
//get host
$host = gethostbyaddr($_SERVER['REMOTE_ADDR']);
//get fisited page
$filevisited = $_REQUEST['f'];
if($filevisited ==""){
    $filevisited = $_SERVER["PHP_SELF"];
}
//get referring page
$referring = urldecode($_REQUEST['r']);
if($referring ==""){
    $referring = $_SERVER["HTTP_REFERER"];
}
//log visit
$log->logvisit($ip, $host, $filevisited,  $referring);
?>

To ensure bot’s are not logged add Disallow directive to the robots.txt.

robots.txt (example)

User-Agent: *
Allow: /
Disallow: /logger/

Next place this code before the end < /body >tag on every page you want to track.

Display Daily Hits with jQuery and Raphael.js

First make sure that you are including jquery.js and raphael.js files.

include(class.logger.php);
$log = new logger();                  //Instentiate the class
$log->tablename = "logger";       //set logger table name
$log->dbconnect();                     //connect to database

//set date and year text for the info bubble that appears on hover
$date = date('F')." ".date('Y');
//load raphael analytics javascript
//Parameters: "table[class], div[id], text[date], width, height, left gutter, bottom gutter, top gutter,stroke color, fontcolor,  fill color,  grid color
$log->loadraphael('data','holder',$date, 600, 250, 20, 30, 30,'#474747', '#000', '#eee', '#eee');
//load table hits containing data
//Parameters: month, year, div[id], table[class]
$log->dailyhits(11, 2009, 'holder', 'data');

(Optional) Example CSS file to position and size the “holder”

#holder {
    width: 600px;
    height: 450px;
    left: 10px;
    position: absolute;
    top: 10px;
}

Display Hits Summary

//show total uniqe visitors to date(parameter: (int) number of months). For example: 3 will start counting from 3 monhts before today.
echo $log->totalunique(3);
//show total visits to date(parameter: (int) number of months). For example: 3 will start counting from 3 monhts before today.
echo $log->totalhits(3);
//visit details (parameter (int) number of last visits to display. For example :100 will show last 100 visits
//the info is displayed in a table with visitdetails[class] for easy CSS styling.
$log->visitdetails(100);

Demo

Download

Do you like this script? Support it by downloading packaged file with an example:

Related Posts:

  • No Related Posts