Главная / Блог ModX / Как сделать счётчик посещений сайта на PHP и MySQL без Метрики

Как сделать свой счётчик просмотров на PHP и MySQL

Если внешний счётчик временно недоступен, владелец сайта может сохранить хотя бы базовую статистику просмотров в собственной базе данных. Для этого необязательно писать полноценную систему аналитики. Достаточно небольшого JavaScript-фрагмента, PHP-обработчика и таблицы MySQL.

Важное ограничение: пример ниже считает просмотры страниц, а не уникальных людей и не сеансы. Мы не будем выдавать каждую запись за отдельного посетителя. Он подходит для обучения и небольшого собственного проекта после настройки защиты.

Браузер отправляет просмотр на свой PHP-endpoint, который сохраняет запись в MySQL.

Какие данные будем собирать

  • время просмотра;
  • адрес страницы без query-параметров;
  • домен источника перехода, если браузер его передал.

IP, идентификаторы пользователей, cookies, значения форм и полные URL с UTM-параметрами в примере не сохраняются. Это снижает объём данных, но не отменяет необходимость соблюдать применимые требования к обработке данных.

Шаг 1. Создаём таблицу MySQL

Используйте отдельную таблицу в базе, доступ к которой есть только у приложения:

CREATE TABLE site_pageviews (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  path VARCHAR(500) NOT NULL,
  referrer_host VARCHAR(190) NOT NULL DEFAULT '',
  PRIMARY KEY (id),
  KEY created_at (created_at),
  KEY path_prefix (path(190))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

Индекс времени ускоряет отчёты по дням. Для больших объёмов понадобится пересмотреть структуру и агрегирование; не превращайте основную базу MODX в бесконечный журнал событий.

Шаг 2. Отправляем просмотр из браузера

Добавьте код один раз в общий шаблон, после выполнения необходимых условий согласия на аналитику. sendBeacon подходит для простой отправки фонового события, а fetch используется как запасной путь:

<!-- Add only where analytics processing is legally permitted. -->
<script>
(function () {
  var payload = {
    path: location.pathname,
    referrer: document.referrer || ''
  };
  var blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/analytics/collect.php', blob);
  } else {
    fetch('/analytics/collect.php', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
      credentials: 'same-origin',
      keepalive: true
    }).catch(function () {});
  }
})();
</script>

Код не определяет «уникального посетителя». Если человек обновил страницу, будет ещё один просмотр. Если бот выполняет JavaScript, его просмотры тоже могут попасть в базу.

Шаг 3. Принимаем данные в PHP

Создайте endpoint /analytics/collect.php. Для подключения к базе используйте переменные окружения PV_DSN, PV_DB_USER и PV_DB_PASSWORD; например DSN: mysql:host=127.0.0.1;dbname=YOUR_DB;charset=utf8mb4. Пароли не должны попадать в веб-каталог или общедоступную статью.

// /public_html/analytics/collect.php — minimal educational example.
// Configure PV_DSN, PV_DB_USER and PV_DB_PASSWORD outside web root.
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405); exit;
}
if ((int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 4096) {
    http_response_code(413); exit;
}
header('Cache-Control: no-store');
$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$host = strtolower((string)($_SERVER['HTTP_HOST'] ?? ''));
$originHost = strtolower((string)(parse_url($origin, PHP_URL_HOST) ?: ''));
if ($origin && $originHost !== preg_replace('/:\\d+$/', '', $host)) {
    http_response_code(403); exit;
}
$data = json_decode((string)file_get_contents('php://input'), true);
if (!is_array($data)) { http_response_code(400); exit; }
$path = (string)($data['path'] ?? '');
if (!$path || $path[0] !== '/' || substr($path, 0, 2) === '//') {
    http_response_code(400); exit;
}
$path = mb_substr(strtok($path, "?#") ?: '/', 0, 500, 'UTF-8');
$refHost = strtolower((string)(parse_url((string)($data['referrer'] ?? ''), PHP_URL_HOST) ?: ''));
$refHost = preg_match('/^[a-z0-9.-]{1,190}$/', $refHost) ? $refHost : '';

try {
    $pdo = new PDO(getenv('PV_DSN'), getenv('PV_DB_USER'), getenv('PV_DB_PASSWORD'), [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]);
    $stmt = $pdo->prepare('INSERT INTO site_pageviews (path, referrer_host) VALUES (?, ?)');
    $stmt->execute([$path, $refHost]);
    http_response_code(204);
} catch (Throwable $e) {
    error_log('Pageview insert failed');
    http_response_code(503);
}

Это минимальная демонстрация. Проверка Origin защищает только от части браузерных сценариев — злоумышленник может отправить запрос напрямую. Для реального сайта добавьте ограничение частоты на уровне сервера, антибот-фильтры, контроль размера событий и мониторинг аномального потока.

Шаг 4. Строим первый отчёт

На первых порах можно вывести количество просмотров по дням обычным SQL. И сразу установить срок хранения сырых записей, например 90 дней:

-- Views per day (not unique visitors):
SELECT DATE(created_at) AS day, COUNT(*) AS views
FROM site_pageviews
WHERE created_at >= NOW() - INTERVAL 7 DAY
GROUP BY DATE(created_at) ORDER BY day;

-- Remove old raw events regularly (e.g. via a daily cron):
DELETE FROM site_pageviews WHERE created_at < NOW() - INTERVAL 90 DAY;

Получается независимый учёт просмотров, но не полноценная замена GA4 или Метрики.

Что учесть при установке на MODX

Код отправки можно поместить в отдельный чанк и подключить в общий шаблон MODX. В отличие от серверного сниппета, который исполняется при формировании HTML и может оказаться закешированным, browser-beacon выполняется при фактическом открытии страницы. Но пользователи с отключённым JavaScript не попадут в такую статистику.

Не подключайте обработчик напрямую к открытой MODX Console и не размещайте секреты в ресурсе или чанке. Для production лучше оформить отдельный контроллер/коннектор с защитами и не зависеть от общего кеша страниц.

Что этот счётчик не умеет

  • не определяет уникальных посетителей;
  • не восстанавливает прошлые визиты;
  • не заменяет вебвизор, цели, атрибуцию рекламы и электронную торговлю;
  • без доработок не защищён от накрутки и больших нагрузок.

Если нужен простой резерв без программирования, посмотрите пять бесплатных аналитических систем. Если важны коммерческие результаты, обязательно настройте отдельный учёт заявок и заказов. А для контроля доступности скрипта есть мониторинг с Telegram.

Вопрос? Ответ

Точно и надёжно — нет. Пример считает просмотры страниц, а не уникальных людей. Для посетителей и сессий нужна отдельная согласованная методика и оценка обработки персональных данных.

Да, если сайт, сервер, база данных и собственный endpoint доступны. Это независимый канал сбора просмотров.

Нет. Это учебный минимальный код. В production нужны фильтрация ботов, лимиты запросов, мониторинг отказов, индексы, политика хранения и защита от накрутки.

Как бы Вы хотели связаться?

Связь с нами