<?php
/*
Copyright (C) 2008 A. Bram Neijt <bram@neijt.nl>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
This programs creates a small SVG hit counter with graph of the last periods.
Hit counts are collected per period and for unique hosts/period.
The period is configurable, see the define statements below.
Homepage: http://logfish.net/pr/svghitcounter/
Needed tables:
CREATE TABLE hitcounter_hitcount(periodid INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, `date` DATE NOT NULL, hits INT UNSIGNED NOT NULL, url TEXT NOT NULL);
CREATE TABLE hitcounter_hitlist(id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, rhost INT UNSIGNED NOT NULL, period INT UNSIGNED NOT NULL, `date` DATE NOT NULL, UNIQUE(rhost, period));
*/
require 'SVGElements.php'; //Simple class containing SVG code
require 'ip2i.php'; //function to calc unsigned int from ip
function validRef($s)
{
return preg_match('/^https?:\/\/.+/', $s); //Add your domain to make sure you only count your domain
}
//Get data and create hitcounter
$period = (isset($_GET['period']) ? intval($_GET['period']) : 0);//Get period in days from GET
$showTotal = isset($_GET['summate']);
//Maximum period of 1 year
if(! ($period >= 1 && $period <= 365))
$period = 7; //Default of 7 days
define('LOGFISH_HITCOUNTER_PERIOD', $period); //$period day periods in graph and counting
define('LOGFISH_HITCOUNTER_PURGE', 9 * LOGFISH_HITCOUNTER_PERIOD); //purge older data
define('LOGFISH_HITCOUNTER_SHOW', 8); //Show 8 periods in graph
define('LOGFISH_HITCOUNTER_WIDTH', 200);
define('LOGFISH_HITCOUNTER_HEIGHT', 80);
$lineColor = isset($_GET['line_color']) ? $_GET['line_color'] : 'blue';
$textColor = isset($_GET['text_color']) ? $_GET['text_color'] : 'black';
$hits = array(0);
$referer = isset($_GET['referer']) ? $_GET['referer'] : false;
if(!validRef($referer))
{
//Try to get the ref from HTTP
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : false;
}
if(!validRef($referer))
$referer = false;
if($referer)
{
//Clean up referer
$referer = preg_replace('/index\\.(html|php|htm|rb|py|rhtml)/', '', $referer);
$referer = preg_replace('/^https/', 'http', $referer);
require 'dbh.php'; //Creates a PDO database handle in $db, if it fails $db === false
//If we have both a referer and a db, then we can do our magic and set the counters
if($dbh)
{
//Yes, finally let's do some magic
//Tables needed:
//CREATE TABLE hitcounter_hitcount(periodid INT UNSIGNED PRIMARY KEY AUTO_INCREMENT, `date` DATE NOT NULL, hits INT UNSIGNED NOT NULL, url TEXT NOT NULL);
//CREATE TABLE hitcounter_hitlist(id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT, rhost INT UNSIGNED NOT NULL, period INT UNSIGNED NOT NULL, `date` DATE NOT NULL, UNIQUE(rhost, period));
//Get the period id for this period and page
$stmt = $dbh->prepare('SELECT periodid FROM hitcounter_hitcount WHERE DATE_SUB(CURDATE(), INTERVAL '.LOGFISH_HITCOUNTER_PERIOD.' DAY) < `date` AND url=? LIMIT 1');
$stmt->bindParam(1, $referer);
$stmt->execute();
$periodid = $stmt->fetch(PDO::FETCH_NUM);
if($periodid)
$periodid = $periodid[0];
else
{
//Create new period, nothing matches the current date interval
$stmt = $dbh->prepare('INSERT INTO hitcounter_hitcount(`date`, hits, url) VALUES(NOW(), 0, ?)');
$stmt->bindParam(1, $referer);
if(!$stmt->execute())
die('Could not insert period, weird internal error here.');
$periodid = $dbh->lastInsertId();
}
//Add hit to ip hitlist
$stmt = $dbh->prepare('INSERT INTO hitcounter_hitlist(rhost, period, `date`) VALUES(?, ?, NOW())');
$stmt->bindParam(1, ip2i($_SERVER['REMOTE_ADDR']));
$stmt->bindParam(2, $periodid);
if($stmt->execute())
{
//Insert OK, we havn't counted this hit yet
$stmt = $dbh->prepare('UPDATE hitcounter_hitcount SET hits=hits+1 WHERE periodid=? LIMIT 1');
$stmt->bindParam(1, $periodid);
$stmt->execute();
}
else
{
//We already counted this host, this period, do nothing, randomly clean up the db
if(rand(0, 50))
{
$stmt = $dbh->prepare('DELETE FROM hitcounter_hitcount WHERE `date` < DATE_SUB(CURDATE(), INTERVAL ? DAY)');
$stmt->bindValue(1, LOGFISH_HITCOUNTER_PURGE, PDO::PARAM_INT);
$stmt->execute();
$stmt = $dbh->prepare('DELETE FROM hitcounter_hitlist WHERE `date` < DATE_SUB(CURDATE(), INTERVAL ? DAY)');
$stmt->bindValue(1, LOGFISH_HITCOUNTER_PURGE, PDO::PARAM_INT);
$stmt->execute();
}
}
//Counting done, now get the graph data
$stmt = $dbh->prepare(
'SELECT hits FROM hitcounter_hitcount WHERE DATE_SUB(CURDATE(), INTERVAL ? DAY) < `date` AND url=? ORDER BY `date` DESC LIMIT ?');
$stmt->bindValue(1, LOGFISH_HITCOUNTER_PERIOD * LOGFISH_HITCOUNTER_SHOW, PDO::PARAM_INT);
$stmt->bindParam(2, $referer);
$stmt->bindValue(3, LOGFISH_HITCOUNTER_SHOW, PDO::PARAM_INT);
if(!$stmt->execute())
{
print_r($stmt);
print_r($stmt->errorInfo());
die('Get data failure');
}
$hits = $stmt->fetchAll(PDO::FETCH_COLUMN);
$stmt = null;
}
}
//Start of output section
//Create SVG hitcounter if we have hits
$dx = LOGFISH_HITCOUNTER_WIDTH / LOGFISH_HITCOUNTER_SHOW;
$max = $hits[0];
$total = 0;
foreach($hits as $hit)
if($hit > $max)
$max = $hit;
$scale = $max ? doubleval(LOGFISH_HITCOUNTER_HEIGHT) / $max : 1;
$path = 'M 0 '.LOGFISH_HITCOUNTER_HEIGHT;
$lastHit = 0;
foreach($hits as $hit)
{
$dy = ($lastHit - $hit) * $scale;
$path .= " l $dx $dy";
$lastHit = $hit;
$total += $hit;
}
$path .= ' L '.LOGFISH_HITCOUNTER_WIDTH.' '.LOGFISH_HITCOUNTER_HEIGHT.' z';
$hitcounter = SVGElements::path($path, $lineColor);
$hitcounter .= SVGElements::link(SVGElements::text(($showTotal ? $total : $hits[0]), $textColor, round(LOGFISH_HITCOUNTER_HEIGHT/1.3), 'start', array('x' => 0, 'y' => round(LOGFISH_HITCOUNTER_HEIGHT/1.1))), 'http://logfish.net/pr/svghitcounter');
header('Content-type: image/svg+xml');
echo '<?xml version="1.0" standalone="no"?>';
?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg viewBox="0 0 <?php echo LOGFISH_HITCOUNTER_WIDTH.' '.LOGFISH_HITCOUNTER_HEIGHT ?>" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>Logfish.net hit counter with <?php echo LOGFISH_HITCOUNTER_PERIOD ?> day graph for <?php echo htmlentities($referer); ?></desc>
<?php echo $hitcounter ?>
</svg>