Файл: new_top/core/classes/Counter.php
Строк: 226
<?php
/**
* Counter — registration of hits and generation of counter images
*/
declare(strict_types=1);
class Counter
{
/** Available visual styles (1..11) */
public static function availableStyles(): array
{
return [
1 => 'Синий',
2 => 'Изумруд',
3 => 'Янтарь',
4 => 'Фиолет',
5 => 'Тёмный',
6 => 'Индиго',
7 => 'Зелёный',
8 => 'Рубин',
9 => 'Серый',
10 => 'Океан',
11 => 'Бронза',
];
}
public static function registerView(int $platformId): ?array
{
try {
$pf = Database::fetch(
'SELECT * FROM platforms WHERE id = ? LIMIT 1',
[$platformId]
);
} catch (Throwable $e) {
return null;
}
if (!$pf || (int)$pf['ban'] === 1) {
return $pf;
}
if ((int)$pf['mode'] === 1) {
return $pf;
}
$ip = client_ip();
$ua = client_ua();
$now = time();
$dayStart = mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y'));
try {
$check = class_exists('AntiCheat')
? AntiCheat::checkHit($platformId, $ip, $ua)
: ['allow' => true, 'reason' => 'ok'];
if (!$check['allow']) {
return $pf;
}
} catch (Throwable $e) {
// count anyway if anti-cheat fails
}
try {
$existsHs = Database::fetchColumn(
'SELECT id FROM hs WHERE pf = ? AND ip = ? AND time >= ? LIMIT 1',
[$platformId, $ip, $dayStart]
);
$isMobile = self::isMobile($ua);
if (!$existsHs) {
Database::query(
'INSERT INTO hs (pf, ip, ua, time) VALUES (?, ?, ?, ?)',
[$platformId, $ip, $ua, $now]
);
$extra = $isMobile ? ', mb = mb + 1' : ', pc = pc + 1';
Database::query(
"UPDATE platforms SET hs = hs + 1, hs_all = hs_all + 1, hs_today = hs_today + 1 {$extra} WHERE id = ?",
[$platformId]
);
$pf['hs'] = (int)$pf['hs'] + 1;
}
Database::query(
'INSERT INTO ht (pf, ip, ua, time) VALUES (?, ?, ?, ?)',
[$platformId, $ip, $ua, $now]
);
Database::query(
'UPDATE platforms SET ht = ht + 1, ht_all = ht_all + 1, ht_today = ht_today + 1 WHERE id = ?',
[$platformId]
);
$pf['ht'] = (int)$pf['ht'] + 1;
$onExists = Database::fetchColumn(
'SELECT id FROM on_log WHERE pf = ? AND ip = ? LIMIT 1',
[$platformId, $ip]
);
if ($onExists) {
Database::query('UPDATE on_log SET time = ?, ua = ? WHERE id = ?', [$now, $ua, $onExists]);
} else {
Database::query(
'INSERT INTO on_log (pf, ip, ua, time) VALUES (?, ?, ?, ?)',
[$platformId, $ip, $ua, $now]
);
}
} catch (Throwable $e) {
// still render image with existing numbers
}
return $pf;
}
public static function registerIn(int $platformId): ?array
{
$pf = Database::fetch(
'SELECT * FROM platforms WHERE id = ? AND ban = 0 AND mode = 0',
[$platformId]
);
if (!$pf) {
return null;
}
$ip = client_ip();
$ua = client_ua();
$now = time();
$dayStart = mktime(0, 0, 0, (int)date('m'), (int)date('d'), (int)date('Y'));
try {
$check = class_exists('AntiCheat')
? AntiCheat::checkIn($platformId, $ip, $ua)
: ['allow' => true, 'reason' => 'ok'];
if ($check['allow']) {
$exists = Database::fetchColumn(
'SELECT id FROM in_log WHERE pf = ? AND ip = ? AND time >= ? LIMIT 1',
[$platformId, $ip, $dayStart]
);
if (!$exists) {
Database::query(
'INSERT INTO in_log (pf, ip, ua, time) VALUES (?, ?, ?, ?)',
[$platformId, $ip, $ua, $now]
);
Database::query(
'UPDATE platforms SET in_count = in_count + 1 WHERE id = ?',
[$platformId]
);
if (class_exists('Points')) {
Points::rewardInbound($platformId);
}
}
}
} catch (Throwable $e) {
}
return $pf;
}
public static function renderImage(?array $pf, string $type = 'big'): void
{
while (ob_get_level() > 0) {
ob_end_clean();
}
$type = ($type === 'small') ? 'small' : 'big';
$width = $type === 'big' ? 88 : 60;
$height = $type === 'big' ? 31 : 20;
if (!$pf) {
self::outputStatus($type, 'mod');
return;
}
if ((int)($pf['ban'] ?? 0) === 1) {
self::outputStatus($type, 'ban');
return;
}
if ((int)($pf['mode'] ?? 0) === 1) {
self::outputStatus($type, 'mod');
return;
}
$hs = (int)($pf['hs'] ?? 0);
$ht = (int)($pf['ht'] ?? 0);
$style = (int)($type === 'big' ? ($pf['style_big'] ?? 1) : ($pf['style_small'] ?? 1));
if ($style < 1 || $style > 11) {
$style = 1;
}
// 1) GD modern draw (most reliable)
if (function_exists('imagecreatetruecolor')) {
self::drawStyledCounter($hs, $ht, $width, $height, $type, $style);
return;
}
// 2) Template GIF without text if present
$gifPath = ASSETS . "/cn/{$type}/{$style}.gif";
if (is_file($gifPath)) {
header('Content-Type: image/gif');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
readfile($gifPath);
return;
}
// 3) Hardcoded minimal GIF
self::outputMinimalGif();
}
private static function outputStatus(string $type, string $status): void
{
$path = ASSETS . "/cn/{$type}/{$status}.gif";
if (is_file($path)) {
header('Content-Type: image/gif');
header('Cache-Control: no-store');
readfile($path);
return;
}
if (function_exists('imagecreatetruecolor')) {
$w = $type === 'big' ? 88 : 60;
$h = $type === 'big' ? 31 : 20;
$img = imagecreatetruecolor($w, $h);
$bg = $status === 'ban'
? imagecolorallocate($img, 120, 30, 30)
: imagecolorallocate($img, 100, 80, 20);
$fg = imagecolorallocate($img, 255, 255, 255);
imagefilledrectangle($img, 0, 0, $w, $h, $bg);
$label = $status === 'ban' ? 'BAN' : 'MOD';
imagestring($img, 3, (int)(($w - imagefontwidth(3) * strlen($label)) / 2), (int)(($h - imagefontheight(3)) / 2), $label, $fg);
header('Content-Type: image/png');
header('Cache-Control: no-store');
imagepng($img);
imagedestroy($img);
return;
}
self::outputMinimalGif();
}
private static function styleColors(int $style): array
{
$map = [
1 => [30, 58, 95, 66, 153, 225],
2 => [16, 70, 60, 0, 200, 160],
3 => [80, 55, 18, 245, 180, 50],
4 => [55, 30, 75, 170, 120, 255],
5 => [18, 18, 28, 100, 100, 120],
6 => [28, 32, 90, 100, 120, 255],
7 => [12, 60, 40, 40, 200, 120],
8 => [90, 25, 35, 255, 90, 110],
9 => [45, 45, 48, 160, 160, 165],
10 => [18, 40, 70, 50, 140, 220],
11 => [70, 42, 20, 220, 160, 80],
];
return $map[$style] ?? $map[1];
}
private static function drawStyledCounter(int $hs, int $ht, int $w, int $h, string $type, int $style): void
{
$c = self::styleColors($style);
$img = imagecreatetruecolor($w, $h);
imagealphablending($img, true);
$bg = imagecolorallocate($img, $c[0], $c[1], $c[2]);
$accent = imagecolorallocate($img, $c[3], $c[4], $c[5]);
$fg = imagecolorallocate($img, 255, 255, 255);
$muted = imagecolorallocate($img, 180, 195, 220);
imagefilledrectangle($img, 0, 0, $w - 1, $h - 1, $bg);
imagefilledrectangle($img, 0, 0, 3, $h - 1, $accent);
$hsStr = (string)$hs;
$htStr = (string)$ht;
$fontFile = ASSETS . '/cn/fonts/font.ttf';
if (!is_file($fontFile)) {
$fontFile = ASSETS . '/cn/fonts/Comfortaa-Light.ttf';
}
if (is_file($fontFile) && function_exists('imagettftext')) {
$size = $type === 'big' ? 9.0 : 7.0;
$y = $type === 'big' ? 21 : 14;
imagettftext($img, $size, 0, 8, $y, $fg, $fontFile, $hsStr);
$sepX = $type === 'big' ? 40 : 28;
imagettftext($img, $size - 1, 0, $sepX, $y, $muted, $fontFile, '/');
imagettftext($img, $size, 0, $sepX + 8, $y, $fg, $fontFile, $htStr);
} else {
$font = $type === 'big' ? 3 : 2;
$text = $hsStr . '/' . $htStr;
$tw = imagefontwidth($font) * strlen($text);
$th = imagefontheight($font);
imagestring($img, $font, (int)max(6, ($w - $tw) / 2), (int)(($h - $th) / 2), $text, $fg);
}
header('Content-Type: image/png');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
imagepng($img);
imagedestroy($img);
}
private static function outputMinimalGif(): void
{
header('Content-Type: image/gif');
header('Cache-Control: no-store');
echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');
}
private static function isMobile(string $ua): bool
{
$ua = strtolower($ua);
foreach (['mobile', 'android', 'iphone', 'ipad', 'ipod', 'webos', 'blackberry', 'opera mini', 'iemobile'] as $n) {
if (strpos($ua, $n) !== false) {
return true;
}
}
return false;
}
public static function embedCodes(int $id, string $baseUrl): array
{
$baseUrl = rtrim($baseUrl, '/');
return [
'big' => '<a href="' . $baseUrl . '/in/' . $id . '" target="_blank" rel="nofollow">'
. '<img src="' . $baseUrl . '/cn/big/' . $id . '" alt="top" width="88" height="31" border="0" /></a>',
'small' => '<a href="' . $baseUrl . '/in/' . $id . '" target="_blank" rel="nofollow">'
. '<img src="' . $baseUrl . '/cn/small/' . $id . '" alt="top" width="60" height="20" border="0" /></a>',
];
}
}