10.4 设计“网站流量统计系统”实例
网站流量是网站管理员最关心的问题之一,包括网站访问数量、访问者来自何方、哪段时间访问网站的客户多一些。网站流量统计系统可以帮助网站管理员解答这些问题。
10.4.1 系统功能分析及数据库设计
网站流量统计系统包含以下主要模块。
• 访问者基本信息。
• 网站综合信息。
• 最近20名访问者信息。
• 按月访问量统计。
• 按年访问量统计。
本系统只对运行的网站进行流量统计,网站的基本信息在系统初始化时,被直接写入到数据库中,没有编辑网站基本信息的网页。
本系统使用的数据库为 FluxStat,在数据库 FluxStat 中需要创建3个表,即表 WebInfo、表Visitors和表FluxStat。
1.表WebInfo
表WebInfo用来保存进行流量统计的网站的基本信息,其结构如表10-10所示。
表10-10 表WebInfo的结构
2.表Visitors
表Visitors用来保存网站最近的20位访问者信息,其结构如表10-11所示。
表10-11 表Visitors的结构
3. 表FluxStat
表FluxStat用来保存网站每天的访问数量,其结构如表10-12所示。
表10-12 表FluxStat的结构
创建数据库和表的脚本为下载源代码的10\FluxStat\FluxStat.sql,可以在phpMyAdmin中执行此脚本。
10.4.2 定义数据库访问类
为了体现出面向对象的程序设计思路,本书实例中将每个表的数据库操作都封装到类中,类与表同名。
本实例中包含3个表,即表WebInfo、表Visitors和表FluxStat。因此创建WebInfo、Visitors和FluxStat 3个类,它们保存在下载源代码的10\FluxStat\Class目录下。
类WebInfo的成员函数如表10-13所示。
表10-13 类WebInfo的成员函数
类Visitors的成员函数如表10-14所示。
表10-14 类Visitors的成员函数
类FluxStat的成员函数如表10-15所示。
表10-15 类FluxStat的成员函数
续表
本章稍后将结合具体的使用来介绍这些代码。
10.4.3 设计函数库
在函数库文件Function.php中包含了本实例程序中经常使用的功能函数,下面对它们进行介绍。
1.GetCurrentTime()
以yyyy-mm-dd hh:MM:ss返回当前的系统时间,代码如下:
function GetCurrentTime() {
$cur_time = getdate();
return $cur_time['year'] . "-" . $cur_time['mon'] . "-" . $cur_time['mday'] . "" . $cur_time['hours'] . ":" . $cur_time['minutes'] . ":" . $cur_time['seconds'];
}
程序调用getdate()函数获取当前的系统时间,结果保存在$cur_time数组中。然后将$cur_time数组中的元素构建成指定格式的字符串,作为函数的返回值。
2.GetExplore()
返回客户端用户使用的浏览器,代码如下:
function GetExplore() {
$explore = "";
$Agent = $_SERVER["HTTP_USER_AGENT"];
//找到第1个;的位置
$pos = strpos($Agent, ';');
if($pos < 0)
return "";
$explore = substr($Agent, $pos+1, strlen($Agent)-$pos); //截取第 1 个分号后面的字符串
//找到第2个;的位置
$pos = strpos($explore, ';');
//第1个分号和第2个分号之间是浏览器信息
$explore = substr($explore, 0, $pos);
return $explore;
}
使用$_SERVER["HTTP_USER_AGENT"]可以返回访问当前网页的客户端信息。例如,客户端使用的操作系统为Windows 7,使用的浏览器为IE 9.0,则$_SERVER["HTTP_USER_AGENT"]的返回值如下:
Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
可以看到,字符串的各个部分被分号分隔开。在第2个分号和第3个分号之间的字符串是浏览器信息。函数GetExplore()正是获取此字符串,并将其返回。
3.GetOSInfo()
获取操作系统信息,代码如下:
function GetOSInfo() {
//在获取客户端的浏览器信息时,包含操作系统信息
$os="";
$Agent = $_SERVER["HTTP_USER_AGENT"];
if (eregi('win',$Agent) && strpos($Agent, '95')) {
$os="Windows 95";
}
elseif (eregi('win 9x',$Agent) && strpos($Agent, '4.90')) {
$os="Windows ME";
}
elseif (eregi('win',$Agent) && ereg('98',$Agent)) {
$os="Windows 98";
}
elseif (eregi('win',$Agent) && eregi('nt 5\.0',$Agent)) {
$os="Windows 2000";
}
elseif (eregi('win',$Agent) && eregi('nt 5\.2',$Agent)) {
$os="Windows 2003";
}
elseif (eregi('win',$Agent) && eregi('nt 5\.1',$Agent)) {
$os="Windows XP";
}
elseif (eregi('win',$Agent) && eregi('nt 6\.1',$Agent)) {
$os="Windows 7";
}
elseif (eregi('win',$Agent) && eregi('32',$Agent)) {
$os="Windows 32";
}
elseif (eregi('win',$Agent) && eregi('nt',$Agent)) {
$os="Windows NT";
}
elseif (eregi('linux',$Agent)) {
$os="Linux";
}
elseif (eregi('unix',$Agent)) {
$os="Unix";
}
elseif (eregi('sun',$Agent) && eregi('os',$Agent)) {
$os="SunOS";
}
elseif (eregi('ibm',$Agent) && eregi('os',$Agent)) {
$os="IBM OS/2";
}
elseif (eregi('Mac',$Agent) && eregi('PC',$Agent)) {
$os="Macintosh";
}
elseif (eregi('PowerPC',$Agent)) {
$os="PowerPC";
}
elseif (eregi('AIX',$Agent)) {
$os="AIX";
}
elseif (eregi('HPUX',$Agent)) {
$os="HPUX";
}
elseif (eregi('NetBSD',$Agent)) {
$os="NetBSD";
}
elseif (eregi('BSD',$Agent)) {
$os="BSD";
}
elseif (ereg('OSF1',$Agent)) {
$os="OSF1";
}
elseif (ereg('IRIX',$Agent)) {
$os="IRIX";
}
elseif (eregi('FreeBSD',$Agent)) {
$os="FreeBSD";
}
if ($os=='')
$os = "Unknown";
return $os;
}
eregi()函数用于实现不区分大小写的正则表达式匹配,语法如下:
int eregi ( string $pattern , string $string [, array &$regs ] )
本函数以$pattern 的规则来解析比对字符串$string。比对结果返回的值放在数组参数 regs 之中,regs[0]内容就是原字符串$string、regs[1]为第一个合乎规则的字符串、regs[2]就是第二个合乎规则的字符串,依此类推。若省略参数 regs,则只是单纯地比对,找到则返回值为true。
程序同样使用$_SERVER["HTTP_USER_AGENT"]返回访问当前网页的客户端信息,解析其中的操作系统字符串,并将其转换为需要的格式。
在页面中引用此文件作为头文件就可以调用里面的函数,代码如下:
Include('Function.php');
10.4.4 设计访问者界面
显示访问者信息的界面文件为Index.php,只要访问者访问此页面,系统就会显示访问者相关的信息,如图10-14所示。
图10-14 直接输入Index.php界面
在“来自”栏中可以看到访问当前网页的来源不同。
获取客户端信息的代码如下:
<?PHP
include('function.php');
include('Class\Visitors.php');
$objVisitor = new Visitors();
//收集需要统计的信息
$theurl = $_SERVER['PHP_SELF']; // 当前的URL
$objVisitor->vIP = $_SERVER['REMOTE_ADDR']; // IP地址
$objVisitor->vOS = GetOSInfo(); // 自定义函数,获取客户端的操作系统
//获取浏览器信息
$objVisitor->vExp = GetExplore();//$arr->parent;
$objVisitor->vRef = $_SERVER['HTTP_REFERER']; //访问前的网址
if($objVisitor->vRef == "")
$objVisitor->vRef = "直接输入或书签导入";
//echo($_SERVER["HTTP_USER_AGENT"]);
//获取当前时间
$objVisitor->vTime = GetCurrentTime();
?>
可以使用$_SERVER['PHP_SELF']语句获取当前访问的网页,使用$_SERVER['REMOTE_ADDR']获取客户端的IP地址,使用$_SERVER['HTTP_REFERER']获取访问此网页之前的页面。获取浏览器和操作系统的函数已经在Function.php中实现。
每次访问者进入 Index.php 界面,系统都要做一次记录,并将记录数据传递给数据库。需要处理的记录包括以下几方面。
1.添加访问者信息到表Visitors中
由于表Visitors中只保存20条最新记录,所以每次记录访问者信息前需要判断是否为20条记录。如果记录总数小于20,则直接插入数据,否则删除编号最小的记录,然后再插入新记录。代码如下:
<?PHP
$nCount = $objVisitor->GetRecordCount();
//保存数据到数据库,数据库中只保存最近访问的20条信息
if($nCount >= 20)
$objVisitor->deleteMinRecord(); // 如果记录数大于20,则删除最小的值
//保存数据
$objVisitor->insert();
?>
Visitors->deleteMinRecord()用于删除编号最小的记录,代码如下:
//删除编号最小的记录
function deleteMinRecord()
{
$sql = "DELETE FROM Visitors WHERE ID IN (SELECT Max(ID) FROM Visitors)";
$this->conn->query($sql);
}
2.更新当天访问量和当月访问量
更新数据库表FluxStat中当天和当月的访问数量。如果数据库中没有当月信息,则创建新记录。代码如下:
<?PHP
//----------------------------------
date_default_timezone_set('Asia/Chongqing'); //系统时间差8小时问题
//更新当天访问量和当月访问量
$cur_time = getdate();
$id = $cur_time['year'] . $cur_time['mon'];//表FluxStat中的Id字段值
$col = "D" . $cur_time['mday'];
//判断当前月的记录是否存在
include('Class\FluxStat.php');
$objFlux = new FluxStat();
if(!$objFlux->exists($id)) {
//没有则插入记录
$objFlux->Id = $id;
$objFlux->insert();
}
else {
$objFlux->updateCount($col, $id);
}
?>
变量$id表示当前年份和月份组成的记录编号,变量$col表示当前系统时间中的日期数字。程序首先调用$objFlux->exists($id)函数,判断当前月份的流量记录是否存在,如果不存在,则插入记录,否则调用$objFlux->updateCount()函数更新当前的流量数据。
$objFlux->updateCount()的代码如下:
//将指定列的值加1,
function updateCount($col, $id)
{
$sql ="UPDATE FluxStat SET " . $col . "=" . $col . "+1, MTotalNum=MTotalNum+1 WHERE Id='" . $id . "'";
$this->conn->query($sql);
}
程序使用UPDATE语句,将指定日期对应的列$col的值增加1,然后将总访问量MTotalNum的值增加1。每当用户访问index.php时,会调用此函数记录访问数量。
3.比较和更新日访问量
表WebInfo中保存着网站日访问量最大值,每次增加日访问量时需要比较当日访问量和日访问量最大值。代码如下:
<?PHP
// --------------------------------
//比较日访问量最大值
$nToday = $objFlux->GetDn($col, $id);
include('Class\WebInfo.php');
$objWeb = new WebInfo();
$nDayMaxNum = $objWeb->GetDayMax(); // 获取WebInfo中的日访问量最大值
//如果当日访问量大于日访问量,则更新记录
if($nToday > $nDayMaxNum)
$objWeb->updateDayMax($nToday, 1);
//--------------------------------
//更新总访问量
$objWeb->increaseTotalNum();
?>
程序首先调用$objFlux->GetDn()函数,获取当前日期的访问量,然后调用$objWeb->GetDayMax()函数获取日访问量的最大值。如果当日访问量大于最大日访问量,则调用$objWeb->updateDayMax()函数,将最大日访问量更新为当日访问量。最后调用$objWeb->increaseTotalNum()函数更新总访问量。
10.4.5 网站信息界面设计
显示网站信息的界面为main.php文件,如图10-15所示。
图10-15 网站信息界面
下面介绍main.php中的主要代码。
1.获取数据
表WebInfo中保存着网站的基本信息,这些信息是在创建数据库时插入的,每次显示时都要重新计算访问天数和日平均访问量,并显示当天访问量。代码如下:
<?PHP
date_default_timezone_set('Asia/Chongqing'); //系统时间差8小时问题
$date = getdate();
$now = $date['year'] . "-" . $date['mon'] . "-" . $date['mday'];
//获取当前系统时间
$nTotalNum = 0;
$nDayMaxNum = 0;
$sWebURL = "";
$sWebName = "";
include('Class\WebInfo.php');
$objWebinfo = new WebInfo();
$objWebinfo->getWebInfo(); //获取网站信息
if($objWebinfo->Id == 0)
exit("请在数据库中输入网站基本信息");
//统计天数=目前时间-统计开始时间
$nStatDays = $objWebinfo->datediff($now, $objWebinfo->StartTime); //计算时间差
if($nStatDays==0)
$nStatDayNum = 1;
//平均日访问量=总访问量/访问天数
if($nStatDays<=0)
$nDayAve = $nTotalNum;
else
$nDayAve = (int)($nTotalNum/$nStatDays);
//得到当天访问量
$nToDayNum=0;
$sYear = $date['year'];
$sMonth = $date['mon'];
$sId = $sYear . $sMonth;
$sDay = $date['mday'];
$sCol = "D" . $sDay;//获取今天对应FluxStat表中的字段名
include('Class\FluxStat.php');
$objFlux = new FluxStat();
$nToDayNum = $objFlux->GetDn($sCol, $sId);
?>
2.显示数据
程序将在表格中显示上面获取到的网站基本信息,代码如下:
<table width="100%" border=1 cellpadding=0 cellspacing=0>
<tbody>
<tr>
<td>
<table border=1 cellpadding=3 cellspacing=0 width="100%" bgcolor="#FFFF99"height="266">
<tbody>
<tr>
<td colspan=4 align="center" bgcolor="#C4E2F0" height="16">
网站综合统计信息
</td>
</tr>
<tr bgcolor="#FFFFFF">
<td align=left colspan=2 height="16">网站名称</td>
<td align=right colspan=2 height="16"><?PHP echo($objWebinfo->WebName); ?></td>
</tr>
<tr bgcolor="#E4E4E4">
<td align=left colspan=2 height="16" >网站网址</td>
<td align=right colspan=2 height="16"><a href="<%=sWebURL%>" target="_blank"><?PHP echo($objWebinfo->WebURL); ?></a></td>
</tr>
<tr bgcolor="#FFFFFF">
<td align=left colspan=2 height="16">总统计天数</td>
<td align=right colspan=2 height="16"><?PHP echo($nStatDays); ?></td>
</tr>
<tr bgcolor="#E4E4E4">
<td align=left colspan=2 height="16">开始统计日期</td>
<td align=right colspan=2 height="16"><?PHP echo($objWebinfo->StartTime); ?></td>
</tr>
<tr bgcolor="#FFFFFF">
<td align=left colspan=2 height="16">总访问量</td>
<td align=right colspan=2 height="16"><?PHP echo($nTotalNum); ?></td>
</tr>
<tr bgcolor="#E4E4E4">
<td align=left colspan=2 height="16">平均日访量</td>
<td align=right colspan=2 height="16"><?PHP echo($nDayAve); ?></td>
</tr>
<tr bgcolor="#FFFFFF">
<td align=left colspan=2 height="15">今日访问量</td>
<td align=right colspan=2 height="15"><?PHP echo($nToDayNum); ?></td>
</tr>
<tr bgcolor="#E4E4E4">
<td align=left colspan=2 height="16">最高日访量</td>
<td align=right colspan=2 height="16"><?PHP echo($objWebinfo->nDayMax); ?></td>
</tr>
<tr align="right">
<td colspan=4 bgcolor="#C4E2F0" height="19">
<p> </p>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
10.4.6 最近访问者界面设计
Visitors.php文件用于显示网站最近20位访问者的信息,运行界面如图10-16所示。
因为本页面的主要功能就是在表格中显示表Visitors的内容,所以这里不对其代码进行具体的分析了。如果链接页面不是“直接输入或书签导入”,则可以进入来源页面。代码如下:
<?PHP
if($sReferer=="直接输入或书签导入")
echo($sReferer);
else {
echo("<a href='" . $sReferer . "' title=" . $sReferer . " target='_blank'>" . substr($sReferer,0, 36) . "</a>");
}
?>
图10-16 最近20位访问者界面
10.4.7 按月统计界面设计
FluxMonth.php页面显示月访问量的人数和百分比图例,如图10-17所示。
图10-17 按月访问量统计
默认统计当前月份的流量信息,显示当前年份和月份的代码如下:
<?PHP
//取得统计月份
$sYear = $_GET["year"];
$sMonth = $_GET["month"];
$date = getdate();
if($sYear=="")
$sYear = $date['year'];
if($sMonth=="")
$sMonth= $date['mon'];
$sId = $sYear . $sMonth;
……
//定义数组值为0,nDArr(30)表示每天访问量
// nPArr(30)表示每天的百分比,nBArr(30)表示图形的长度
for($i=0; $i<=30; $i++) {
$nDArr[$i] = 0;
$nPArr[$i] = 0;
$nBArr[$i] = 0;
}
//取得此月份的访问量信息
$results = $objFlux->load_FluxStat($sId);
$i=0;
if($row = $results->fetch_row()) {
for($i=0; $i<=30; $i++) {
$nDArr[$i] = $row[$i+1];
$nMonthTotalNum = (int)$row[32];
}
}
if($nMonthTotalNum>0) {
for($i=0; $i<=30; $i++) {
$nPArr[$i] = ($nDArr[$i]/$nMonthTotalNum*10000)/100 . "%";
$nBArr[$i] = $nDArr[$i]/$nMonthTotalNum*200;
}
}
for($i=0; $i<=30; $i++) { ?>
<tr bgcolor="#FFFFFF">
<td align=left><?PHP echo($sMonth); ?>月<?PHP echo($i+1); ?>日</td>
<td align=left><?PHP echo($nDArr[$i]); ?></td>
<td align=left><img src="images/bar.gif" width="<?PHP echo($nBArr[$i]); ?>"height="12"><?PHP echo($nPArr[$i]); ?></td>
</tr>
<?PHP } /* end of for */ ?>
</tbody>
</table>
</td>
</tr>
程序首先获取当前系统日期中的年份和月份,构成变量$sId;然后调用load_FluxStat($sId)函数获取指定月份的流量记录,并在表格中显示。定义3个数组分别记录每天的访问量、每天访问量占当月总访问量的百分比以及图形的长度。图形的长度定义代码如下:
<img src="images/bar.gif" width="<?PHP echo($nBArr[$i]); ?> "height="12"><?PHP echo($nPArr[$i]); ?>
请读者参照注释理解。
在FluxMonth.php中,使用表单form1处理用户显示统计数据,定义代码如下:
<form name="form1" action="FluxMonth.php" method="post">
当表单提交时,将执行FluxMonth.php,重新显示。
在表单form1中,并没有提交按钮,那么如何提交表单数据呢?这里的设计思想是每次选择不同的月份时提交表单。为了实现这一功能,在FluxMonth.php中设计了JavaScript函数MonthSubmit(),代码如下:
<Script Language="JavaScript">
function MonthSubmit() {
var sYear,strURL,sMonth;
sYear = document.form1.year.value;
sMonth= document.form1.mn.value;
strURL = "FluxMonth.asp?year=" + sYear + "&month=" + sMonth;
form1.action = strURL;
form1.submit();
}
</Script>
year表示当前的年份,month表示当前的月份,使用它们作为参数访问FluxMonth.asp,就可以显示指定月份的统计数据了。
为了使用户改变月份时,程序自动执行MonthSubmit()函数,可以使用下面的语句定义下拉菜单:
<select name="mn" onChange="MonthSubmit()">
10.4.8 按年统计界面设计
FluxYear.php页面显示年访问量的人数和百分比图例,如图10-18所示。
图10-18 按年度统计界面
通过选择年份的下拉框更改统计的年份来提交表单,年份的下拉框的定义代码如下:
<select name="year" onChange="YearSubmit()">
YearSubmit()函数的功能是提交表单,代码如下:
<Script Language="JavaScript">
function YearSubmit() {
var sYear,strURL;
sYear = document.form1.year.value;
strURL = "FluxYear.asp?year=" + sYear;
form1.action = strURL;
form1.submit();
}
</Script>
按年统计访问量的设计思路与按月统计访问量的思路相似,读者可以参照源代码及注释理解。