Skip to content

Instantly share code, notes, and snippets.

@sagittaracc
Last active April 8, 2021 14:33
Show Gist options
  • Save sagittaracc/2c8f4531cc026568d817e3ef60f72356 to your computer and use it in GitHub Desktop.
Save sagittaracc/2c8f4531cc026568d817e3ef60f72356 to your computer and use it in GitHub Desktop.
Счётчик уникальных посещений на примере скачивания дистрибутива (PHP)
<?php
/**
* ListInterface это интерфейс для классов IPIgnoreList, UserAgentIgnoreList и SearchBotList
*
* Должен реализовать метод `has($item)` для определения принадлежности пользователя к реализуемому списку
*/
interface ListInterface
{
/**
* Задает правило принадлежности пользователя к данному списку
*
* @param mixed $item - какая-либо информация по пользователю
* @return boolean
*/
public function has($item);
}
/**
* IPIgnoreList - это класс для задания списка адресов пользователей которых мы не будем учитывать
*/
class IPIgnoreList implements ListInterface
{
/**
* @var array $ipList - список игнорируемых адресов пользователей
*/
private $ipList;
/**
* Constructor
*
* @param array $ipList
*/
function __construct($ipList)
{
$this->ipList = $ipList;
}
/**
* Возвращает принадлежит ли переданный $ip к списку игнорируемых
*
* @param string $ip
* @return boolean
*/
public function has($ip)
{
return in_array($ip, $this->ipList);
}
}
/**
* UserAgentIgnoreList - это класс для задания списка браузеров которых мы не будем учитывать
*/
class UserAgentIgnoreList implements ListInterface
{
/**
* @var array $keywords - список игнорируемых ключевых слов по которым определяется браузер пользователя
*/
protected $keywords;
/**
* Constructor
*
* @param array $keywords
*/
function __construct($keywords)
{
$this->keywords = $keywords;
}
/**
* Возвращает если это тот браузер который нас не интересует
*
* @param string $userAgent
* @return boolean
*/
public function has($userAgent)
{
$userAgent = strtolower($userAgent);
foreach($this->keywords as $keyword)
{
if(strpos($userAgent, $keyword) !== false)
return true;
}
return false;
}
}
/**
* SearchBotList - это класс для перечисления всевозможных ботов
*/
class SearchBotList extends UserAgentIgnoreList
{
/**
* Constructor
*/
function __construct()
{
parent::__construct([
'bot',
'spider',
'spyder',
'crawlwer',
'walker',
'search',
'yahoo',
'holmes',
'htdig',
'archive',
'tineye',
'yacy',
'yeti',
]);
}
}
/**
* Visit - это класс для определения уникальных посетителей по дням
*/
class Visit
{
/**
* @var string $token - ежедневный токен пользователя
*/
protected $token;
/**
* @var string $id - уникальный идентификатор пользователя
*/
protected $id;
/**
* @const string TOKEN_NAME - название куки для $token
*/
const TOKEN_NAME = 'VisitToken';
/**
* @const string ID_NAME - название куки для $id
*/
const ID_NAME = 'VisitId';
/**
* Constructor
*
* Генерирует ежедневный токен для пользователя
*/
function __construct()
{
$this->token = $this->getToken();
}
public function getUser()
{
return $this;
}
/**
* Возвращает если сегодняшнее посещение является новым
* Если ежедневная кука пользователя не установлена или если она вчерашняя
*
* @return boolean
*/
public function isNew()
{
return !isset($_COOKIE[self::TOKEN_NAME])
|| $_COOKIE[self::TOKEN_NAME] !== $this->token;
}
/**
* Очищает старые токены перед тем как перезаписать новые
*/
public function clearToken()
{
unset($_COOKIE[self::TOKEN_NAME]);
unset($_COOKIE[self::ID_NAME]);
}
/**
* Генерирует и сохраняет новую пару ежедневных токенов
* Хранит сутки пока не наступит новый день
*/
public function saveToken()
{
setcookie(self::TOKEN_NAME, $this->getToken(), time() + 60 * 60 * 24);
$this->id = uniqid();
setcookie(self::ID_NAME, $this->id, time() + 60 * 60 * 24);
}
/**
* Возвращает токен исходя из IP адреса пользователя и текущего дня
*
* @return string
*/
protected function getToken()
{
return md5($_SERVER['REMOTE_ADDR'] . date('Ymd'));
}
/**
* Возвращает уникальный идентификатор пользователя
* Возвращает только что сгенерированный или тот что был передан самим пользователем
*
* @return string
*/
public function getId()
{
if (isset($_COOKIE[self::ID_NAME]))
{
$this->id = $_COOKIE[self::ID_NAME];
}
return $this->id;
}
}
/**
* Download - это класс для подсчета уникальных скачек дистрибутивов
*
* Если загрузка файлов реализована через общий скрипт то загрузку можно рассматривать как посещение данной страницы
* К ежедневному определению уникальности также добавляется параметр build дистрибутива
* Так как в один и тот же день можно одним пользователем скачать разные билды - что будет считаться уникальным посещением
*/
class Download extends Visit
{
/**
* @var string $build - строковое представление уникальности файла (версия или название)
*/
private $build;
/**
* Constructor
*
* @param string $build
*/
function __construct($build)
{
$this->build = $build;
parent::__construct();
}
/**
* Возвращает токен но с учетом билда
*
* @return string
*/
protected function getToken()
{
return md5(parent::getToken() . $this->build);
}
}
/****************************************************
* Example: *
****************************************************/
$searchBotList = new SearchBotList();
if ($searchBotList->has($_SERVER['HTTP_USER_AGENT']))
return;
$ipIgnoreList = new IPIgnoreList([
'127.0.0.1'
]);
if ($ipIgnoreList->has($_SERVER['REMOTE_ADDR']))
return;
// DNT - Do Not Track header
if (isset($_SERVER['HTTP_DNT']) && $_SERVER['HTTP_DNT'] == "1")
return;
$download = new Download("build 4.1.7");
if ($download->getUser()->isNew())
{
$download->getUser()->clearToken();
$download->getUser()->saveToken();
}
//Данный уникальный идентификатор пользователя нужно сохранить чтобы потом по нему подсчитать уникальные загрузки
echo($download->getUser()->getId());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment