最近开发了一款Zblog插件,文煞网站日志统计分析工具。在开发的时候遇到一个很有意思的事情,统计页面需要获取多种数据,比如蜘蛛、游客或登录用户的PV和IP数量,由于需要统计的数据比较多,而且还需要按照指定的字段进行排序展示,如果全部从mysql进行查询的话,那么该页面就需要对数据库进行十至二十次的查询,明显增加了服务器压力。在我巧妙的构思下,找到了一个比较好的方法,那就是先取出所有日志数据,然后再用取出的数据来获取我想要的内容!这样我只需要查询一次数据库!
一、从数组中获取指定字段数据个数

因为我们已经取出了网站日志的所有数据,现在需要展示一个IP请求榜,按照同IP请求的次数进行排序,展示到页面,我们可以直观的看到访问频率最高的IP,然后就可以对该IP的访问行为进行一些判断和识别。
function ips($array) {
if($array != null) {
$ipss = array();
foreach ($array as $v) {
$ips[] = $v['ip'];
}
// 创建一个计数数组
$ipCounts = array_count_values($ips);
foreach ($ipCounts as $ip => $count) {
$ipss[] = array('ip'=>$ip,'num'=>$count);
}
$ips[]=array();
usort($ipss, function($a, $b) {
return $b['num'] - $a['num'];
}
);
return $ipss;
} else {
return array();
}
}代码注释:因为我们想从数组$array中对不同IP的出现次数进行排序,那么我们首先需要一个计数数组,本代码是构造了一个新的数组,里面只包含了IP字段和num字段,num字段对应的值是IP出现次数。然后我们直接用foreach输出ips($array)就可以了。
二、按照指定字段的值对数组进行排序
接着上面的代码进行介绍,这里我们还通过以下代码按照num字段的值进行了降序排序,就是上面代码中的以下部分代码:
$ips[]=array();
usort($ipss, function($a, $b) {
return $b['num'] - $a['num'];
}
);三、从数组中删除符合条件的数据
function de404logs($array){
global $zbp;
$groups_404 = array();
foreach ($array as $item) {
if ($item['code'] == 404) {
$groups_404[] = $item;
}
}
$newArray = [];
// 删除符合条件的数组
$newArray = array_map(function ($item) {
$item['code'] = 200; //这里
return $item;
}, $groups_404);
foreach ($newArray as $value) {
if (($key = array_search($value, $array)) !== false) {
unset($array[$key]);
}
}
$array = array_merge($array, $newArray); // 重新合并数组
return $array;
}代码注释:这里我们先找到符合条件的数据(代码中是code字段为404的数据),并存入了新的数组$groups_404,然后通过循环$groups_404,把$groups_404中的code从404改为200并存入入新数组$newArray,然后通过array_merge($array, $newArray)合并数组,并把两个数组交集的部分(就是符合其他字段的值与$groups_404相同,但是code字段已经修改为200的newArray数组)进行删除。
由于我还要通过$groups_404修改数据库,中间删掉了操作数据库的代码。所以没有直接使用$groups_404数组。
四、从一个数组中按照指定字段的范围值提取数据
function wenshaweblogs_time($data, $timePeriod) {
// 定义时间常量
$todayStart = strtotime('today 00:00:00');
$todayEnd = strtotime('today 23:59:59');
$weekStart = strtotime('this week Monday');
$weekEnd = strtotime('this week Sunday 23:59:59');
$monthStart = strtotime('first day of this month');
$monthEnd = strtotime('last day of this month 23:59:59');
// 定义时间段
switch ($timePeriod) {
case 0: // 本日
$mintime = $todayStart;
$maxtime = $todayEnd;
break;
case 1: // 本周
$mintime = $weekStart;
$maxtime = $weekEnd;
break;
case 2: // 本月
$mintime = $monthStart;
$maxtime = $monthEnd;
break;
case 3: // 上周
$mintime = strtotime('last week Monday');
$maxtime = strtotime('last week Sunday 23:59:59');
break;
case 4: // 上个月
$mintime = strtotime('first day of last month');
$maxtime = strtotime('last day of last month 23:59:59');
break;
case 5: // 本年
$mintime = strtotime("first day of January " . date('Y'));
$maxtime = $todayEnd;
break;
case 6: // 上一年
$mintime = strtotime("first day of January " . date('Y', strtotime('-1 year')));
$maxtime = strtotime("last day of December " . date('Y', strtotime('-1 year')) . " 23:59:59");
break;
default:
throw new Exception("Invalid time period: " . $timePeriod);
}
// 过滤数组并返回结果数组
if ($data != null) {
return array_filter($data, function ($item) use ($mintime, $maxtime) {
return $item['time'] >= $mintime && $item['time'] <= $maxtime;
});
} else {
return array();
}
}代码注释:wenshaweblogs_time($data, $timePeriod)通过传入$data和$timePeriod对$data进行数据提取,代码中可以看出我们是通过time字段进行范围匹配,从而返回符合时间范围的新的数据!
五、总结
以上代码都需要在一个页面进行处理,而且还有很多类似处理的数据的地方,本来我们可以直接通过条件查询数据库,而无需像我这样通过处理数组来达到自己的目的。但是由于我们需要查询全部数据来进行总数的统计和区分,既然已经获取了全部数据,我下面的数据就不需要再去查询数据库了,而是直接在前面查询到的总数数据中进行各式各样的处理。虽然我没测试过哪种方法效果更好,但是少查询数据库总是没毛病的,尤其是数据库和脚本不再同一个服务器的情况下,少查询数据库应该更佳!
本文只是部分代码片段分享,希望对你有所帮助!








