[2021] | Matomo Heartbeat

header('Content-Type: application/json'); echo json_encode($result); <!DOCTYPE html> <html> <head> <!-- Matomo Tracking Code --> <script> var _paq = window._paq = window._paq || []; _paq.push(['trackPageView']); _paq.push(['enableLinkTracking']); (function() var u="https://your-matomo-domain.com/"; _paq.push(['setTrackerUrl', u+'matomo.php']); _paq.push(['setSiteId', '1']); var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; g.async=true; g.src=u+'matomo.js'; s.parentNode.insertBefore(g,s); )(); </script>

// Example usage in your tracking endpoint if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_GET['action']) && $_GET['action'] === 'heartbeat') $handler = new MatomoHeartbeatHandler( 'https://your-matomo-domain.com', 1, // Your site ID 'your-token-auth' // Optional for API access ); matomo heartbeat

$data = json_decode(file_get_contents('php://input'), true); $result = $tracker->recordHeartbeat( session_id(), $data['visitor_id'] ?? '', $data['page_url'] ?? '', $data['engaged_time'] ?? 0 ); 0 ); // Auto-initialize if Matomo is present

// Auto-initialize if Matomo is present if (typeof window._paq !== 'undefined') window.matomoHeartbeat = new MatomoHeartbeat( heartbeatInterval: 15, minVisitLength: 5, idleTimeout: 30, debug: window.location.hostname === 'localhost' ); debug: window.location.hostname === 'localhost' )

startIdleMonitoring() this.idleCheckId = setInterval(() => const idleTime = (Date.now() - this.lastActivity) / 1000; if (idleTime >= this.options.idleTimeout && this.isActive) this.stopHeartbeat(); this.log(`User idle for $idleTime seconds, stopping heartbeat`); , 1000);

public function recordHeartbeat($sessionId, $visitorId, $pageUrl, $engagedTime = 0) $stmt = $this->db->prepare(" SELECT id, total_engaged_time, last_heartbeat FROM matomo_heartbeat_sessions WHERE session_id = ? AND is_active = 1 "); $stmt->execute([$sessionId]); $session = $stmt->fetch(PDO::FETCH_ASSOC); $now = new DateTime(); if ($session) // Update existing session $lastHeartbeat = new DateTime($session['last_heartbeat']); $interval = $now->getTimestamp() - $lastHeartbeat->getTimestamp(); // Validate heartbeat interval if ($interval <= $this->heartbeatTimeout) $newTotalTime = $session['total_engaged_time'] + min($engagedTime, $interval); $update = $this->db->prepare(" UPDATE matomo_heartbeat_sessions SET last_heartbeat = ?, total_engaged_time = ?, heartbeat_count = heartbeat_count + 1 WHERE id = ? "); $update->execute([$now->format('Y-m-d H:i:s'), $newTotalTime, $session['id']]); return ['status' => 'updated', 'total_time' => $newTotalTime]; else // Session expired $this->endSession($session['id']); return $this->createNewSession($sessionId, $visitorId, $pageUrl); else // Create new session return $this->createNewSession($sessionId, $visitorId, $pageUrl);