Файл: new_top/core/classes/Stats.php
Строк: 376
<?php
/**
* Statistics helpers — daily series, device split, online
*/
declare(strict_types=1);
class Stats
{
/** Last N days labels (Y-m-d) */
public static function dayLabels(int $days = 14): array
{
$labels = [];
for ($i = $days - 1; $i >= 0; $i--) {
$labels[] = date('Y-m-d', strtotime("-{$i} days"));
}
return $labels;
}
/**
* Daily unique hosts from hs table for platform (0 = all)
* @return array date => count
*/
public static function dailyHosts(int $platformId = 0, int $days = 14): array
{
$since = strtotime('-' . ($days - 1) . ' days 00:00:00');
$sql = 'SELECT FROM_UNIXTIME(time, "%Y-%m-%d") AS d, COUNT(DISTINCT ip) AS c
FROM hs WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
$sql .= ' GROUP BY d ORDER BY d';
$rows = Database::fetchAll($sql, $params);
$map = [];
foreach ($rows as $r) {
$map[$r['d']] = (int)$r['c'];
}
$out = [];
foreach (self::dayLabels($days) as $d) {
$out[$d] = $map[$d] ?? 0;
}
return $out;
}
/** Daily total hits from ht */
public static function dailyHits(int $platformId = 0, int $days = 14): array
{
$since = strtotime('-' . ($days - 1) . ' days 00:00:00');
$sql = 'SELECT FROM_UNIXTIME(time, "%Y-%m-%d") AS d, COUNT(*) AS c
FROM ht WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
$sql .= ' GROUP BY d ORDER BY d';
$rows = Database::fetchAll($sql, $params);
$map = [];
foreach ($rows as $r) {
$map[$r['d']] = (int)$r['c'];
}
$out = [];
foreach (self::dayLabels($days) as $d) {
$out[$d] = $map[$d] ?? 0;
}
return $out;
}
/** Device split for platform or global */
public static function deviceSplit(int $platformId = 0): array
{
if ($platformId > 0) {
$row = Database::fetch('SELECT mb, pc FROM platforms WHERE id = ?', [$platformId]);
return [
'mobile' => (int)($row['mb'] ?? 0),
'pc' => (int)($row['pc'] ?? 0),
];
}
$row = Database::fetch('SELECT COALESCE(SUM(mb),0) AS mb, COALESCE(SUM(pc),0) AS pc FROM platforms');
return [
'mobile' => (int)($row['mb'] ?? 0),
'pc' => (int)($row['pc'] ?? 0),
];
}
public static function onlineCount(int $platformId = 0, int $windowSec = 600): int
{
$sql = 'SELECT COUNT(*) FROM on_log WHERE time > ?';
$params = [time() - $windowSec];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
return (int) Database::fetchColumn($sql, $params);
}
public static function topPlatforms(int $limit = 10): array
{
return Database::fetchAll(
'SELECT id, url, hs, ht, mb, pc FROM platforms WHERE mode = 0 AND ban = 0 ORDER BY hs DESC LIMIT ' . (int)$limit
);
}
public static function summary(): array
{
return [
'platforms' => (int) Database::fetchColumn('SELECT COUNT(*) FROM platforms'),
'active' => (int) Database::fetchColumn('SELECT COUNT(*) FROM platforms WHERE mode = 0 AND ban = 0'),
'banned' => (int) Database::fetchColumn('SELECT COUNT(*) FROM platforms WHERE ban = 1'),
'mod' => (int) Database::fetchColumn('SELECT COUNT(*) FROM platforms WHERE mode = 1'),
'users' => (int) Database::fetchColumn('SELECT COUNT(*) FROM user'),
'hs' => (int) Database::fetchColumn('SELECT COALESCE(SUM(hs),0) FROM platforms'),
'ht' => (int) Database::fetchColumn('SELECT COALESCE(SUM(ht),0) FROM platforms'),
'in' => (int) Database::fetchColumn('SELECT COALESCE(SUM(in_count),0) FROM platforms'),
'online' => self::onlineCount(),
'cats' => (int) Database::fetchColumn('SELECT COUNT(*) FROM cat'),
];
}
/** Short day labels for charts (d.m) */
public static function shortLabels(array $ymdKeys): array
{
$out = [];
foreach ($ymdKeys as $d) {
$out[] = date('d.m', strtotime($d));
}
return $out;
}
/** Hits by hour for today (0..23) for platform or global */
public static function hourlyHits(int $platformId = 0): array
{
$dayStart = mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y'));
$sql = 'SELECT HOUR(FROM_UNIXTIME(time)) AS h, COUNT(*) AS c FROM ht WHERE time >= ?';
$params = [$dayStart];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
$sql .= ' GROUP BY h';
$rows = Database::fetchAll($sql, $params);
$map = [];
foreach ($rows as $r) {
$map[(int)$r['h']] = (int)$r['c'];
}
$out = [];
for ($h = 0; $h < 24; $h++) {
$out[$h] = $map[$h] ?? 0;
}
return $out;
}
/** Rank position by hosts among active platforms */
public static function rankPosition(int $platformId): int
{
$pf = Database::fetch('SELECT hs, ht FROM platforms WHERE id = ? AND mode = 0 AND ban = 0', [$platformId]);
if (!$pf) {
return 0;
}
$better = (int) Database::fetchColumn(
'SELECT COUNT(*) FROM platforms WHERE mode = 0 AND ban = 0 AND (hs > ? OR (hs = ? AND ht > ?) OR (hs = ? AND ht = ? AND id < ?))',
[(int)$pf['hs'], (int)$pf['hs'], (int)$pf['ht'], (int)$pf['hs'], (int)$pf['ht'], $platformId]
);
return $better + 1;
}
public static function latestNews(int $limit = 5): array
{
try {
return Database::fetchAll(
'SELECT * FROM news WHERE active = 1 ORDER BY time_add DESC LIMIT ' . (int)$limit
);
} catch (Throwable $e) {
return [];
}
}
/** Daily inbound clicks from in_log */
public static function dailyIn(int $platformId = 0, int $days = 14): array
{
$since = strtotime('-' . ($days - 1) . ' days 00:00:00');
$sql = 'SELECT FROM_UNIXTIME(time, "%Y-%m-%d") AS d, COUNT(*) AS c FROM in_log WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
$sql .= ' GROUP BY d';
$rows = Database::fetchAll($sql, $params);
$map = [];
foreach ($rows as $r) {
$map[$r['d']] = (int)$r['c'];
}
$out = [];
foreach (self::dayLabels($days) as $d) {
$out[$d] = $map[$d] ?? 0;
}
return $out;
}
/** Hits by weekday (1=Mon..7=Sun) for last N days */
public static function weekdayHits(int $platformId = 0, int $days = 28): array
{
$since = strtotime('-' . ($days - 1) . ' days 00:00:00');
$sql = 'SELECT DAYOFWEEK(FROM_UNIXTIME(time)) AS dw, COUNT(*) AS c FROM ht WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
$sql .= ' GROUP BY dw';
$rows = Database::fetchAll($sql, $params);
// MySQL DAYOFWEEK: 1=Sun .. 7=Sat → remap to Mon-first labels
$byMysql = [];
foreach ($rows as $r) {
$byMysql[(int)$r['dw']] = (int)$r['c'];
}
// order Mon..Sun → mysql 2,3,4,5,6,7,1
$order = [2, 3, 4, 5, 6, 7, 1];
$labels = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
$data = [];
foreach ($order as $i => $dw) {
$data[$labels[$i]] = $byMysql[$dw] ?? 0;
}
return $data;
}
public static function periodTotals(int $platformId, int $days): array
{
$hosts = self::dailyHosts($platformId, $days);
$hits = self::dailyHits($platformId, $days);
$ins = self::dailyIn($platformId, $days);
$sumH = array_sum($hosts);
$sumT = array_sum($hits);
$sumI = array_sum($ins);
$ctr = $sumT > 0 ? round($sumI / $sumT * 100, 2) : 0.0;
return [
'hosts' => $sumH,
'hits' => $sumT,
'in' => $sumI,
'ctr' => $ctr,
];
}
/** Save today's ranks for all active platforms */
public static function snapshotRanks(): int
{
$day = date('Y-m-d');
$list = Database::fetchAll(
'SELECT id, hs, ht FROM platforms WHERE mode = 0 AND ban = 0 ORDER BY hs DESC, ht DESC, id ASC'
);
$n = 0;
foreach ($list as $i => $p) {
$rank = $i + 1;
Database::query(
'INSERT INTO rank_snap (pf, day, rank, hs, ht) VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE rank = VALUES(rank), hs = VALUES(hs), ht = VALUES(ht)',
[(int)$p['id'], $day, $rank, (int)$p['hs'], (int)$p['ht']]
);
$n++;
}
return $n;
}
/** Rank delta vs yesterday snapshot (negative = rose) */
public static function rankDelta(int $platformId): ?array
{
$today = self::rankPosition($platformId);
$y = date('Y-m-d', strtotime('-1 day'));
try {
$prev = Database::fetch('SELECT rank, hs FROM rank_snap WHERE pf = ? AND day = ?', [$platformId, $y]);
} catch (Throwable $e) {
$prev = null;
}
if (!$prev) {
return ['rank' => $today, 'delta' => null, 'prev' => null];
}
$prevRank = (int)$prev['rank'];
return [
'rank' => $today,
'prev' => $prevRank,
'delta' => $prevRank - $today, // positive = improved (moved up)
];
}
public static function categorySummary(): array
{
return Database::fetchAll(
'SELECT c.id, c.name,
COUNT(p.id) AS pf_cnt,
COALESCE(SUM(p.hs),0) AS hs,
COALESCE(SUM(p.ht),0) AS ht,
COALESCE(SUM(p.in_count),0) AS inn
FROM cat c
LEFT JOIN platforms p ON p.cat_id = c.id AND p.mode = 0 AND p.ban = 0
GROUP BY c.id, c.name
ORDER BY hs DESC, c.sort'
);
}
/** Platforms with no hits in last N days */
public static function stalePlatforms(int $days = 7, int $limit = 30): array
{
$since = time() - $days * 86400;
return Database::fetchAll(
'SELECT p.id, p.url, p.hs, p.ht, p.user_id, p.time_add
FROM platforms p
WHERE p.mode = 0 AND p.ban = 0
AND NOT EXISTS (SELECT 1 FROM ht WHERE ht.pf = p.id AND ht.time >= ?)
ORDER BY p.time_add DESC
LIMIT ' . (int)$limit,
[$since]
);
}
public static function movers(int $limit = 10): array
{
$y = date('Y-m-d', strtotime('-1 day'));
try {
$rows = Database::fetchAll(
'SELECT s.pf, s.rank AS prev_rank, p.url, p.hs, p.ht
FROM rank_snap s
INNER JOIN platforms p ON p.id = s.pf AND p.mode = 0 AND p.ban = 0
WHERE s.day = ?
ORDER BY p.hs DESC
LIMIT 200',
[$y]
);
} catch (Throwable $e) {
return ['up' => [], 'down' => []];
}
$up = [];
$down = [];
foreach ($rows as $r) {
$cur = self::rankPosition((int)$r['pf']);
if ($cur < 1) {
continue;
}
$delta = (int)$r['prev_rank'] - $cur;
$item = [
'id' => (int)$r['pf'],
'url' => $r['url'],
'rank' => $cur,
'prev' => (int)$r['prev_rank'],
'delta' => $delta,
];
if ($delta > 0) {
$up[] = $item;
} elseif ($delta < 0) {
$down[] = $item;
}
}
usort($up, static function ($a, $b) { return $b['delta'] <=> $a['delta']; });
usort($down, static function ($a, $b) { return $a['delta'] <=> $b['delta']; });
return [
'up' => array_slice($up, 0, $limit),
'down' => array_slice($down, 0, $limit),
];
}
/** Global daily hosts/hits for admin overview */
public static function globalDaily(int $days = 14): array
{
return [
'hosts' => self::dailyHosts(0, $days),
'hits' => self::dailyHits(0, $days),
'in' => self::dailyIn(0, $days),
];
}
/** Top referrers approx from in_log by platform (most inbound) */
public static function topByIn(int $limit = 10): array
{
return Database::fetchAll(
'SELECT id, url, in_count, hs, ht FROM platforms WHERE mode = 0 AND ban = 0 ORDER BY in_count DESC LIMIT ' . (int)$limit
);
}
/** New platforms registered last N days */
public static function newPlatformsCount(int $days = 7): int
{
$since = time() - $days * 86400;
return (int) Database::fetchColumn(
'SELECT COUNT(*) FROM platforms WHERE time_add >= ?',
[$since]
);
}
/** Hits last N hours (rolling) */
public static function hitsLastHours(int $hours = 24, int $platformId = 0): int
{
$since = time() - $hours * 3600;
$sql = 'SELECT COUNT(*) FROM ht WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
return (int) Database::fetchColumn($sql, $params);
}
/** Unique hosts last N hours */
public static function hostsLastHours(int $hours = 24, int $platformId = 0): int
{
$since = time() - $hours * 3600;
$sql = 'SELECT COUNT(DISTINCT ip) FROM ht WHERE time >= ?';
$params = [$since];
if ($platformId > 0) {
$sql .= ' AND pf = ?';
$params[] = $platformId;
}
return (int) Database::fetchColumn($sql, $params);
}
/** Peak hour today */
public static function peakHour(int $platformId = 0): array
{
$hourly = self::hourlyHits($platformId);
$peakH = 0;
$peakV = 0;
foreach ($hourly as $h => $v) {
if ($v > $peakV) {
$peakV = $v;
$peakH = (int)$h;
}
}
return ['hour' => $peakH, 'hits' => $peakV];
}
/** CTR for platform: in / hits * 100 today-ish using totals */
public static function ctr(int $platformId): float
{
$pf = Database::fetch('SELECT ht, in_count FROM platforms WHERE id = ?', [$platformId]);
if (!$pf || (int)$pf['ht'] < 1) {
return 0.0;
}
return round((int)$pf['in_count'] / (int)$pf['ht'] * 100, 2);
}
/** Growth % hosts today vs yesterday from logs */
public static function hostGrowth(int $platformId = 0): array
{
$today = self::dailyHosts($platformId, 2);
$keys = array_keys($today);
if (count($keys) < 2) {
return ['today' => 0, 'yesterday' => 0, 'pct' => 0];
}
$y = (int)$today[$keys[0]];
$t = (int)$today[$keys[1]];
$pct = $y > 0 ? round(($t - $y) / $y * 100, 1) : ($t > 0 ? 100.0 : 0.0);
return ['today' => $t, 'yesterday' => $y, 'pct' => $pct];
}
/** User totals for all their platforms */
public static function userSummary(int $userId): array
{
$row = Database::fetch(
'SELECT COUNT(*) AS n,
COALESCE(SUM(hs),0) AS hs,
COALESCE(SUM(ht),0) AS ht,
COALESCE(SUM(in_count),0) AS inc,
COALESCE(SUM(hs_all),0) AS hs_all,
COALESCE(SUM(ht_all),0) AS ht_all
FROM platforms WHERE user_id = ? AND ban = 0',
[$userId]
);
return $row ?: ['n' => 0, 'hs' => 0, 'ht' => 0, 'inc' => 0, 'hs_all' => 0, 'ht_all' => 0];
}
/** Combined daily for all platforms of a user */
public static function userDaily(int $userId, int $days = 14): array
{
$ids = Database::fetchAll('SELECT id FROM platforms WHERE user_id = ? AND ban = 0', [$userId]);
$labels = self::dayLabels($days);
$hosts = array_fill_keys($labels, 0);
$hits = array_fill_keys($labels, 0);
$ins = array_fill_keys($labels, 0);
foreach ($ids as $row) {
$id = (int)$row['id'];
foreach (self::dailyHosts($id, $days) as $d => $v) {
$hosts[$d] = ($hosts[$d] ?? 0) + (int)$v;
}
foreach (self::dailyHits($id, $days) as $d => $v) {
$hits[$d] = ($hits[$d] ?? 0) + (int)$v;
}
foreach (self::dailyIn($id, $days) as $d => $v) {
$ins[$d] = ($ins[$d] ?? 0) + (int)$v;
}
}
return ['hosts' => $hosts, 'hits' => $hits, 'in' => $ins];
}
}