Отслеживание нажатий с помощью PHP: базовые и продвинутые подходы

Раздел: Веб-разработка -> Трекинг в PHP

Трекинг кликов в PHP: обзор подходов

Отслеживание действий пользователя, в частности кликов, решает задачи анализа поведения, конверсии и безопасности. В PHP эта функциональность реализуется как на стороне сервера, так и через взаимодействие с клиентской частью. Ниже рассмотрены основные методы с акцентом на производительность и удобство сопровождения.

Как обеспечить трекинг без перезагрузки страницы?

Наиболее эффективный способ - асинхронный HTTP-запрос (AJAX) к PHP-скрипту, который сохраняет данные в реляционной базе данных (MySQL). Такой подход не нарушает пользовательский опыт и позволяет фиксировать любые события.

Пошаговая реализация

  1. Создание таблицы для хранения кликов:
CREATE TABLE clicks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    url VARCHAR(500) NOT NULL,
    timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
    user_agent VARCHAR(255),
    session_id VARCHAR(64)
);

Click track php (трекинг кликов в php)

  1. PHP-обработчик (track.php):
<?php
$pdo = new PDO('mysql:host=localhost;dbname=analytics', 'user', 'pass');
$stmt = $pdo->prepare('INSERT INTO clicks (url, user_agent, session_id) VALUES (?, ?, ?)');
$stmt->execute([
    $_POST['url'] ?? '',
    $_SERVER['HTTP_USER_AGENT'] ?? '',
    session_id()
]);
header('Content-Type: application/json');
echo json_encode(['status' => 'ok']);
  1. JavaScript на странице:
document.addEventListener('click', function(e) {
    fetch('track.php', {
        method: 'POST',
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        body: 'url=' + encodeURIComponent(window.location.href)
    });
});

Возможные проблемы и решения

  • Потеря сессии при параллельных запросах: сессии PHP по умолчанию блокируются. Для асинхронного трекинга следует закрыть сессию сразу после чтения идентификатора: session_write_close().
  • CSRF-атаки: если трекер используется для критичных действий, добавьте проверку токена. Внутренний трекинг редко требует защиты, так как запросы только на запись без влияния на данные.
  • Пропуск запросов при большом трафике: используйте асинхронное подключение к БД или очереди (см. вариант с очередью).

Как записывать клики в файл для быстрого прототипа?

Самый простой вариант - дописывать данные в текстовый файл. Подходит для отладки или низконагруженных проектов.

<?php
$data = date('Y-m-d H:i:s') . ' | ' . ($_GET['url'] ?? 'unknown') . "\n";
file_put_contents('clicks.log', $data, FILE_APPEND | LOCK_EX);

Проблемы:

  • При конкурентном доступе возможна потеря данных, если явно не установлена блокировка (LOCK_EX).
  • Файл быстро разрастается, анализ затруднён.

Как отследить клик с помощью пиксельного трекера (1x1 GIF)?

Метод используется в email-рассылках и простых счетчиках. Клик по ссылке ведет на PHP-скрипт, который записывает событие и возвращает прозрачное изображение.

<?php
// pixel.php?url=...
$trackUrl = $_GET['url'] ?? '';
file_put_contents('clicks.log', date('c') . ' ' . $trackUrl . PHP_EOL, FILE_APPEND);
header('Content-Type: image/gif');
echo base64_decode('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7');

Ссылка на трекере: <a href="pixel.php?url=target.php">ссылка</a>

Ограничения:

  • Только GET-запросы, данные доступны в URL.
  • Не подходит для отслеживания произвольных кликов без редиректа.

Как обработать высоконагруженный трекинг с минимальной задержкой?

Для масштабируемых решений используют очередь сообщений (RabbitMQ, Redis). PHP-скрипт отправляет данные в очередь, а отдельный воркер записывает их в базу.

// producer.php
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$redis->lPush('clicks', json_encode(['url' => $_POST['url'], 'time' => microtime(true)]));
// worker.php (запускается постоянно)
$pdo = new PDO('mysql:...');
$redis = new Redis();
while ($data = $redis->brPop('clicks', 5)) {
    $click = json_decode($data[1], true);
    $pdo->prepare('INSERT INTO clicks (url, ts) VALUES (?, FROM_UNIXTIME(?))')->execute([$click['url'], $click['time']]);
}

Сложность:

  • Требуется установка и поддержка Redis/RabbitMQ.
  • Воркер должен быть стабильным (супервизор, systemd).

Как внедрить готовое решение без написания кода с нуля?

Системы веб-аналитики (Matomo, Open Web Analytics) предоставляют PHP-библиотеку для трекинга. Установка через Composer:

composer require matomo/matomo-php-tracker
use MatomoTracker;
$matomo = new MatomoTracker($idSite, $matomoUrl);
$matomo->doTrackPageView('Название страницы');
$matomo->doTrackEvent('Category', 'Action', 'Label');

Недостатки:

  • Зависимость от внешнего сервиса.
  • Необходимость настройки конфиденциальности (GDPR).

Расширенные примеры реализации трекинга кликов

Пример 1: трекинг с защитой от дублирования и сессиями

PHP-скрипт проверяет, был ли клик уже зафиксирован за текущую сессию по определённому URL, чтобы не раздувать базу.

Пример
<?php
session_start();
$url = $_POST['url'] ?? '';
if (!$url || isset($_SESSION['clicked_urls'][$url])) {
    http_response_code(204);
    exit;
}
$_SESSION['clicked_urls'][$url] = true;

$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$stmt = $pdo->prepare('INSERT INTO clicks (url, user_agent, session_id) VALUES (?, ?, ?)');
$stmt->execute([$url, $_SERVER['HTTP_USER_AGENT'], session_id()]);
echo json_encode(['status' => 'recorded']);
Результат: В таблицу clicks добавляется запись только при первом клике по данному URL в сессии. Повторные запросы возвращают 204 No Content.

Пример 2: трекинг с использованием JSON-логгера и ротации файлов

Для тех случаев, когда база данных недоступна, можно использовать структурированные логи в формате JSON, которые затем обрабатываются с помощью Logstash или другого парсера.

Пример
<?php
function logClick($url, $userId = null) {
    $logEntry = [
        'timestamp' => date('c'),
        'url' => $url,
        'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
        'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
        'user_id' => $userId
    ];
    $logFile = '/var/log/clicks/' . date('Y-m-d') . '.jsonl';
    $dir = dirname($logFile);
    if (!is_dir($dir)) {
        mkdir($dir, 0755, true);
    }
    file_put_contents($logFile, json_encode($logEntry) . PHP_EOL, FILE_APPEND | LOCK_EX);
}
logClick($_POST['url'] ?? 'direct');
Результат: Создаётся файл 2025-03-26.jsonl, содержащий одну строку JSON на событие. Пример строки:
{"timestamp":"2025-03-26T12:00:00+00:00","url":"http://example.com/page","user_agent":"Mozilla/5.0 ...","ip":"192.168.1.1","user_id":null}

Пример 3: асинхронная запись через MySQL с использованием INSERT DELAYED (устарело) и альтернативы

В версиях MySQL до 5.6 существовал INSERT DELAYED, который немедленно возвращал управление, а запись производилась в фоне. Сейчас вместо этого можно использовать таблицы с ENGINE=MEMORY и скрипт, периодически сбрасывающий данные на диск. Пример: создание витрины в оперативной памяти и последующее копирование в InnoDB.

Пример
CREATE TABLE clicks_memory (
    url VARCHAR(500),
    ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=MEMORY;

-- PHP-скрипт записи
$pdo->query('INSERT INTO clicks_memory (url) VALUES ("' . addslashes($url) . '")');

-- Фоновый скрипт переноса (cron каждые 5 минут)
$pdo->query('INSERT INTO clicks_innodb SELECT * FROM clicks_memory');
$pdo->query('DELETE FROM clicks_memory');
Результат: Запись кликов происходит быстро, так как ENGINE=MEMORY не использует дисковые операции. Однако при сбое сервера данные теряются. Данная техника полезна для временного хранения с последующей агрегацией.

Пример 4: трекинг с использованием Bitwise флагов для подсчета уникальных кликов

Для систем с ограниченными ресурсами можно записывать факт клика в битовую маску, храненную в Redis. Это позволяет быстро подсчитать уникальное количество кликов на страницу без хранения каждого события.

Пример
$redis = new Redis();
$redis->connect('127.0.0.1');
$pageId = $_POST['page_id'] ?? 0;
$userId = session_id();
// Используем битовое поле: каждый бит соответствует дню месяца
$bitOffset = (int)date('j'); // день месяца 1-31
$redis->setBit('clicks:'.$pageId, $bitOffset, 1);
$redis->sAdd('users:'.$pageId, $userId); // для подсчета уникальных посетителей
Результат: В Redis создаётся строка clicks:123 с битовой маской, где установлен бит в позиции текущего дня. Поле users:123 хранит set идентификаторов сессий. Подсчёт уникальных дней выполняется через BITCOUNT clicks:123. Этот подход использует минимальное количество памяти.

Пример 5: обработка ошибок при недоступности БД – отложенная запись в файл и повторная попытка

Пример
<?php
try {
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass', [
        PDO::ATTR_TIMEOUT => 2
    ]);
    $pdo->prepare('INSERT INTO clicks (url) VALUES (?)')->execute([$_POST['url']]);
} catch (PDOException $e) {
    // Сохраняем в файл-буфер
    $buffer = '/tmp/click_buffer.txt';
    file_put_contents($buffer, serialize(['url' => $_POST['url'], 'time' => time()]) . PHP_EOL, FILE_APPEND);
    http_response_code(202); // запрос принят, но не обработан
}
// Фоновый скрипт восстанавливает данные из буфера
if (file_exists('/tmp/click_buffer.txt')) {
    $lines = file('/tmp/click_buffer.txt', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    $pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
    foreach ($lines as $line) {
        $data = unserialize($line);
        $pdo->prepare('INSERT INTO clicks (url, ts) VALUES (?, FROM_UNIXTIME(?))')->execute([$data['url'], $data['time']]);
    }
    unlink('/tmp/click_buffer.txt');
}
Результат: При сбое БД запрос получает HTTP 202 и данные не теряются, а помещаются в буфер. Восстановление происходит позже (по крону или при следующем запросе). Это повышает надёжность системы трекинга.

Трекинг кликов в PHP - comments

En
Click track php (php)