PHP методы отслеживания кликов в email-письмах: подробное руководство
Основной метод: скрипт-редирект с реляционной базой данных
Для достоверного учета переходов по ссылкам из писем применяется архитектура, при которой все ссылки в письме заменяются на адрес PHP-скрипта, который записывает событие в базу и затем перенаправляет пользователя на целевой ресурс. База данных обеспечивает структурированное хранение и возможность дальнейшего анализа.
Создание таблицы click_log:
CREATE TABLE click_log (
id INT AUTO_INCREMENT PRIMARY KEY,
campaign_id VARCHAR(50) NOT NULL,
subscriber_id VARCHAR(100) NOT NULL,
url TEXT NOT NULL,
clicked_at DATETIME DEFAULT CURRENT_TIMESTAMP,
ip_address VARCHAR(45) NOT NULL,
user_agent TEXT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Pub mail click php (отслеживание кликов в почтовых рассылках php)
Генерация ссылки в письме (пример с использованием base64 и подписи):
$data = $campaign_id . '|' . $subscriber_id . '|' . $target_url;
$token = hash_hmac('sha256', $data, SECRET_KEY);
$encoded_url = base64_encode($target_url);
$link = "https://example.com/track.php?c=$campaign_id&s=$subscriber_id&u=$encoded_url&t=$token";
Обработка в track.php:
$campaign_id = $_GET['c'];
$subscriber_id = $_GET['s'];
$url = base64_decode($_GET['u']);
$token = $_GET['t'];
$expected = hash_hmac('sha256', $campaign_id.'|'.$subscriber_id.'|'.$url, SECRET_KEY);
if (!hash_equals($expected, $token)) die('Invalid link');
$stmt = $pdo->prepare('INSERT INTO click_log (campaign_id,subscriber_id,url,ip_address,user_agent) VALUES (?,?,?,?,?)');
$stmt->execute([$campaign_id, $subscriber_id, $url, $_SERVER['REMOTE_ADDR'], $_SERVER['HTTP_USER_AGENT']]);
header('Location: '.$url);
exit;
Проблема: Если база данных недоступна, пользователь не будет перенаправлен. Решение: использовать транзакцию или сначала записывать в очередь, а затем в БД асинхронно. Другая проблема: производительность при высокой нагрузке. Решение: использовать пул соединений или репликацию.
Как записывать клики в файл без использования базы данных?
Для небольших проектов или временного мониторинга можно вести лог в текстовом файле. Каждый клик дописывается новой строкой.
$timestamp = date('Y-m-d H:i:s');
$line = implode('|', [$timestamp, $subscriber_id, $url, $_SERVER['REMOTE_ADDR']]);
file_put_contents('clicks.log', $line.PHP_EOL, FILE_APPEND | LOCK_EX);
Проблема: При параллельных запросах возможна потеря данных или повреждение файла. Решение: Использовать блокировку (LOCK_EX), но это снижает производительность. Альтернатива - запись в системный лог (syslog).
Как получать информацию о кликах через вебхуки от почтового провайдера?
Если используется ESP (например, Mailgun, SendGrid, SparkPost), они могут отправлять HTTP-запросы на ваш сервер при нажатии на ссылку. Вам нужно только обработать входящий POST/GET.
// Пример обработчика для Mailgun (POST с JSON)
$data = json_decode(file_get_contents('php://input'), true);
if ($data['event'] === 'clicked') {
$url = $data['url'];
$recipient = $data['recipient'];
$timestamp = $data['timestamp'];
// сохранить в вашу базу
}
Проблема: Необходимо защитить конечную точку от поддельных запросов. Решение: проверять подпись (например, по API-ключу), проверять IP отправителя.
Как использовать NoSQL базу для отслеживания кликов?
MongoDB или Redis могут быть хорошим выбором для высоконагруженных систем. Пример с MongoDB (Php библиотека).
$collection = (new MongoDB\Client)->email->clicks;
$collection->insertOne([
'campaign_id' => $campaign_id,
'subscriber_id' => $subscriber_id,
'url' => $url,
'time' => new MongoDB\BSON\UTCDateTime(),
'ip' => $_SERVER['REMOTE_ADDR']
]);
Проблема: Отсутствие транзакций может привести к неконсистентности при сбоях. Решение: Использовать подтверждения записи (write concern).
Расширенные примеры реализации
Полный пример генерации отслеживаемых ссылок и обработки с использованием базы данных MySQL и PDO:
<?php
// config
$secretKey = 'your-secret-key-here';
$dbHost = 'localhost';
$dbName = 'email_marketing';
$dbUser = 'root';
$dbPass = '';
$baseTrackUrl = 'https://track.example.com/click.php';
// Функция создания отслеживаемой ссылки
function createTrackLink($campaignId, $subscriberId, $targetUrl) {
global $secretKey, $baseTrackUrl;
$data = $campaignId . '|' . $subscriberId . '|' . $targetUrl;
$token = hash_hmac('sha256', $data, $secretKey);
$urlEncoded = base64_encode($targetUrl);
return $baseTrackUrl . '?c=' . urlencode($campaignId) . '&s=' . urlencode($subscriberId) . '&u=' . urlencode($urlEncoded) . '&t=' . $token;
}
// Пример использования при отправке письма
$campaignId = 'camp_202503';
$subscriberId = 'user_12345';
$realUrl = 'https://example.com/offer';
$trackableLink = createTrackLink($campaignId, $subscriberId, $realUrl);
echo "Ссылка для письма: $trackableLink\n";
?>
Ссылка для письма: https://track.example.com/click.php?c=camp_202503&s=user_12345&u=aHR0cHM6Ly9leGFtcGxlLmNvbS9vZmZlcg==&t=8f3b...
Полный код click.php:
<?php
session_start();
require 'config.php'; // содержит $pdo
$c = $_GET['c'] ?? '';
$s = $_GET['s'] ?? '';
$uEncoded = $_GET['u'] ?? '';
$t = $_GET['t'] ?? '';
// Декодирование URL
$url = base64_decode($uEncoded);
if ($url === false) { header('HTTP/1.0 400 Bad Request'); exit; }
// Проверка подписи
$expected = hash_hmac('sha256', $c.'|'.$s.'|'.$url, $secretKey);
if (!hash_equals($expected, $t)) {
// Логируем попытку подделки
error_log('Invalid click token: '.$c.' '.$s);
header('HTTP/1.0 403 Forbidden');
exit;
}
// Подготовка и выполнение запроса с использованием prepared statements
$stmt = $pdo->prepare('INSERT INTO click_log (campaign_id, subscriber_id, url, ip_address, user_agent) VALUES (?,?,?,?,?)');
$stmt->execute([
$c,
$s,
$url,
$_SERVER['REMOTE_ADDR'],
$_SERVER['HTTP_USER_AGENT'] ?? ''
]);
// Перенаправление
header('Location: '.$url, true, 302);
exit;
?>
Пример анализа статистики: вывод количества кликов по каждой кампании за последние 7 дней.
<?php
$stmt = $pdo->query("SELECT campaign_id, COUNT(*) as clicks FROM click_log WHERE clicked_at >= NOW() - INTERVAL 7 DAY GROUP BY campaign_id ORDER BY clicks DESC");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo "<table><tr><th>Кампания</th><th>Клики</th></tr>";
foreach ($results as $row) {
echo "<tr><td>".htmlspecialchars($row['campaign_id'])."</td><td>".$row['clicks']."</td></tr>";
}
echo "</table>";
?>
(выведет таблицу с данными)
Пример использования очереди Redis для отложенной записи (чтобы не ждать базу при редиректе).
// click.php с отправкой в очередь
$redis = new Redis();
$redis->connect('127.0.0.1');
$logData = [
'campaign_id' => $c,
'subscriber_id' => $s,
'url' => $url,
'ip' => $_SERVER['REMOTE_ADDR'],
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'time' => date('c')
];
$redis->lpush('click_queue', json_encode($logData));
header('Location: '.$url);
Код потребителя (worker.php), запускаемый как отдельный процесс:
<?php
while (true) {
$data = $redis->brpop('click_queue', 0);
$log = json_decode($data[1], true);
$stmt = $pdo->prepare('INSERT INTO click_log (campaign_id,subscriber_id,url,ip_address,user_agent,clicked_at) VALUES (?,?,?,?,?,NOW())');
$stmt->execute([$log['campaign_id'], $log['subscriber_id'], $log['url'], $log['ip'], $log['user_agent']]);
}
?>