WP Manifestindependent plugin directory
manifest / analytics / wp-visitors-tracker

WP Visitors Tracker

A comprehensive, privacy-focused visitor tracking and analytics plugin for WordPress that provides detailed insights into your website traffic without compromising performance or user privacy.

by Avniloff Avraham · github.com/avniloff/wp-visitors-tracker · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/avniloff/wp-visitors-tracker/archive/refs/heads/main.zip

WP Visitors Tracker — Developer Documentation

This README documents the key files, classes, functions, hooks, objects, and constants in the plugin. It’s organized per file so you can quickly find behavior and extension points.

  • Minimum WP: 6.5
  • Minimum PHP: 8.1
  • Text domain: wpvt

Contents

  • wpvt.php
  • autoload.php
  • src/Core/PluginBootstrap.php
  • src/Core/Activation.php
  • src/Core/Deactivation.php
  • src/Helpers/VisitorTypes.php
  • src/Application/VisitProcessor.php
  • src/Services/Detection/ClientIpResolver.php
  • src/Services/Detection/UserAgentParser.php
  • src/Services/Detection/VisitContextCollector.php
  • src/Services/Sessions/SessionService.php
  • src/Repository/VisitRepository.php
  • src/Admin/Controller/AdminAjaxController.php
  • src/Admin/Controller/ClientAjaxController.php
  • src/Assets/pages/dashboard-page.php
  • src/Assets/js/admin.js
  • src/Assets/js/frontend-collector.js
  • src/Assets/css/admin.css

Release Preparation (Minimal)

  1. Bump version in wpvt.php (header + WPVT_VERSION).
  2. Update CHANGELOG.md with a new top section.
  3. Ensure dev generator is disabled in production:
  • Keep WPVT_DEV_GENERATOR = false (default) and WP_DEBUG off.
  1. Exclude non-distributable artifacts when packaging (optional for GitHub source): tests/, docker-compose.yml, Dockerfile.
  2. Tag release:
  • git tag v1.0.7 && git push origin v1.0.7
  1. Draft GitHub Release → paste changelog for that version.
  2. Smoke test on clean WP:
  • Activate → visit front page → confirm a row in wpvt_visits.
  • Send UA curl/7.x to trigger hard ban.
  • Repeat soft UA (e.g. MyBot crawler/1.0) twice to trigger soft ban.

Key Filters Exposed:

  • wpvt_ban_soft_repeats, wpvt_ban_soft_window_sec, wpvt_ban_default_ttl_minutes, wpvt_ban_hard_ttl_minutes
  • wpvt_session_ttl, wpvt_session_fingerprint_recover_window

Dev Generator Gating: Loaded only if WPVT_DEV_GENERATOR true OR WP_DEBUG true. Example to enable locally:

define('WPVT_DEV_GENERATOR', true);

wpvt.php

Main plugin bootstrap.

  • Constants
    • WPVT_VERSION — current plugin version string.
    • WPVT_PLUGIN_FILE, WPVT_PLUGIN_DIR, WPVT_PLUGIN_URL, WPVT_PLUGIN_BASENAME — paths/URLs for plugin.
  • Behavior
    • PHP version guard (>= 8.1): shows admin notice and deactivates the plugin if unmet.
    • Requires autoload.php.
    • Boots the plugin on plugins_loaded via WPVT\Core\PluginBootstrap::getInstance().
  • Hooks
    • register_activation_hook(__FILE__, [Activation::class, 'activate']).
    • register_deactivation_hook(__FILE__, [Deactivation::class, 'deactivate']).
    • register_uninstall_hook(__FILE__, [Deactivation::class, 'uninstall']).
    • add_action('plugins_loaded', ...).

autoload.php

Simple PSR‑4 autoload for the WPVT\ namespace, mapping to src/.

  • Functions
    • spl_autoload_register(callable) — resolves WPVT\\... to src/... .php and requires if the file exists.

src/Core/PluginBootstrap.php

Primary plugin singleton. Wires hooks, admin UI, and front‑end assets.

  • Class: WPVT\Core\PluginBootstrap
    • Properties
      • private static ?PluginBootstrap $instance — singleton instance.
      • private ?Application\VisitProcessor $orchestrator — lazy orchestration root.
      • private Repository\VisitRepository $VisitRepository
      • private Services\Detection\VisitContextCollector $VisitContextCollector
      • private Services\Sessions\SessionService $SessionService
    • Methods
      • public static function getInstance(): PluginBootstrap — lazy singleton accessor.
      • private function __construct() — builds core services and calls init().
      • private function init(): void — registers WP hooks; conditionally loads AJAX handlers; enqueues assets.
      • public function trackFrontVisit(): void — processes the current request via orchestrator on template_redirect (after canonical redirects).
      • public function onShutdown(): void — cleanup expired sessions.
      • public function enqueueFrontendScripts(): void — enqueues frontend-collector.js and localizes wpvt_ajax (url, nonce, session_id).
      • public function enqueueAdminScripts(string $hook): void — enqueues admin CSS/JS on plugin page; localizes wpvt_ajax (url, nonce).
      • private function isTrackingEnabled(): bool — reads wpvt_tracking_enabled option (default true).
      • private function getOrchestrator(): Application\VisitProcessor — lazy-creates orchestrator.
      • public function addAdminMenu(): void — adds top‑level admin menu page and settings subpage.
      • public function initAdminSettings(): void — registers settings actually used.
    • public function renderAdminPage(): void — requires src/Assets/pages/dashboard-page.php.
      • public function renderSettingsPage(): void — requires src/Assets/pages/settings-page.php.
      • private function __clone() — disabled cloning.
      • public function __wakeup(): void — throws to prevent unserialize.
    • Hooks Registered
      • parse_request (early bot blocking), template_redirect (priority 99) for tracking, shutdown, wp_enqueue_scripts, admin_menu, admin_init, admin_enqueue_scripts.

src/Core/Activation.php

Activation and schema management.

  • Class: WPVT\Core\Activation
    • Methods
      • public static function activate(): void — creates tables/options and runs migrations.
      • public static function migrateDatabase(): void — inspects tables with DESCRIBE and runs ALTER TABLE for structure alignment (enum types, indexes, column additions/removals, widths). Targets tables: wpvt_sessions, wpvt_visits, wpvt_visitors.
      • Table creators (use dbDelta):
        • private static function createSystemLogsTable($wpdb): void
        • private static function createVisitsTable($wpdb): void
        • private static function createSessionsTable($wpdb): void
        • private static function createVisitorsTable($wpdb): void
        • private static function createGeoCacheTable($wpdb): void
        • private static function createBansTable($wpdb): void
      • private static function createDefaultOptions(): void — adds wpvt_tracking_enabled.
    • Schema Notes
      • All tables: utf8mb4_unicode_ci, InnoDB, sensible indexes (session, url, timestamps).

src/Core/Deactivation.php

Deactivation and uninstall cleanup.

  • Class: WPVT\Core\Deactivation
    • Methods
      • public static function deactivate(): void — deletes wpvt_tracking_enabled, flushes cache, logs deactivation time to option wpvt_deactivated_at (and error_log when WP_DEBUG).
      • public static function uninstall(): void — deletes option(s), drops all plugin tables, removes uploaded files under /uploads/wpvt/, and flushes cache.
      • private static function clearCache(): voidwp_cache_flush().
      • private static function logDeactivation(): void — updates timestamp and logs in debug mode.
      • private static function dropTables(): voidDROP TABLE IF EXISTS for all plugin tables.
      • private static function removeUploads(): void — removes uploads dir recursively.
      • private static function removeDirectory(string $dir): bool — recursive delete helper.

src/Core/VisitorTypes.php

Centralized visitor type constants and validation helpers.

  • Class: WPVT\Core\VisitorTypes
    • Constants: HUMAN, BOT, DEFAULT = HUMAN.
    • Methods
      • public static function validate($visitorType): string — returns a valid type or default.
      • public static function isValid($visitorType): bool — membership check.

src/Application/VisitProcessor.php

Atomic request handling and coordination of detection, session management, and logging.

  • Class: WPVT\Application\VisitProcessor
    • Constructor
      • __construct(Services\Detection\VisitContextCollector, Logger\VisitRepository, Services\Sessions\SessionService) — DI wiring.
    • Methods
      • public function handleRequest(): bool — detects visit; if skippable returns true; otherwise starts DB transaction, gets/creates session, logs visit, commits; on exceptions rolls back and logs error.
      • public function cleanup(): int — delegates to SessionService::cleanupExpiredSessions().

src/Services/Detection/ClientIpResolver.php

Resolves the best-guess client IP from headers.

  • Class: WPVT\Services\Detection\ClientIpResolver
    • Methods
      • public function getIpAddress(): string — checks headers in order (HTTP_X_FORWARDED_FOR, HTTP_X_REAL_IP, HTTP_CLIENT_IP, REMOTE_ADDR), takes first IP (first item if a list), prefers public IPs, falls back to any valid IP, defaults to 127.0.0.1.

src/Services/Detection/UserAgentParser.php

User Agent and browser metadata extraction.

  • Class: WPVT\Services\Detection\UserAgentParser
    • Methods
      • public function getUserAgent(): string — reads $_SERVER['HTTP_USER_AGENT'], trims to 500 chars.
      • public function getScreenResolution(): ?string — reads wpvt_screen_resolution cookie; validates ^\d{1,5}x\d{1,5}$.
      • public function getBrowserLanguage(): ?string — parses HTTP_ACCEPT_LANGUAGE, returns two‑letter code or null.
      • public function detectVisitorType(): string — naive bot check by UA substrings (bot, crawler, spider, scraper).
      • public function getBrowserDisplay(?string $userAgent = null, ?string $browserType = null): array — returns display name/class using explicit browserType first or UA matching (Edge/Firefox/Opera/Safari/Chrome/Unknown).

src/Services/Detection/VisitContextCollector.php

Aggregates detection logic and decides whether to track the request.

  • Class: WPVT\Services\Detection\VisitContextCollector
    • Properties: lazy ClientIpResolver and UserAgentParser.
    • Methods
      • public function detectVisit(): ?array — returns a structured array with ip_address, user_agent, url, referer, screen_resolution, browser_language, visitor_type, browser_type, or null if should not track.
      • private function shouldTrackRequest(): bool — excludes admin (/wp-admin), admin-ajax.php, REST (/wp-json/), cron/login/xmlrpc, static assets by extension, and logged‑in admins.
      • private function getRequestUrl(): string — from REQUEST_URI, sanitized.
      • private function getReferer(): string — from HTTP_REFERER, validated URL or empty.
      • private function detectBrowserType(): ?string — UA parser that prefers Edge, then Firefox/Opera/Safari, then Chrome, else unknown.

src/Services/Sessions/SessionService.php

Short‑lived session management tied to visits.

  • Class: WPVT\Services\Sessions\SessionService
    • Constants
      • private const SESSION_DURATION = 43200 — seconds (12 hours by default; override via wpvt_session_ttl).
    • Properties
      • private string $sessionsTable — resolved from $wpdb->prefix.
    • Methods
      • public function getOrCreateSession(array $visitData): ?string — validates IP/UA; queries the latest active, unexpired session by ip_address + user_agent; updates it or creates a new one; returns session_id.
      • private function createNewSession(array $visitData): ?string — inserts new row; returns session_id.
      • private function updateSession(string $sessionId, array $visitData): void — refreshes last_activity, extends expires_at, increments page_views.
      • private function generateSessionId(): stringbin2hex(random_bytes(32)).
      • public function getSessionData(string $sessionId): ?array — row fetch by session_id when active.
      • public function cleanupExpiredSessions(): int — sets is_active = 0 for expired sessions; returns affected count.

src/Repository/VisitRepository.php

Atomic visit logging with counter semantics.

  • Class: WPVT\Repository\VisitRepository
    • Methods
      • private function sanitizeTextField($value, string $fallback = ''): string — wrapper around sanitize_text_field with fallback.
      • public function logVisit(array $visitData, string $sessionId): bool — validates inputs; sanitizes; executes INSERT ... ON DUPLICATE KEY UPDATE into wpvt_visits keyed by (session_id, url)
        • On insert: sets counts to 1 and timestamps to NOW.
        • On duplicate: increments visit_count, updates last_visit, and conditionally updates screen_resolution, browser_language, browser_type.

src/Admin/Controller/AdminAjaxController.php

Authenticated admin AJAX handlers (PSR-4 controller).

  • Registers:
    • wp_ajax_wpvt_clear_visits (delegates to VisitRepository::truncateVisits())
    • Settings toggles and soft heuristics updates
    • IP details via InsightsService
    • Ban CRUD (IP, CIDR, UA) via BansService

src/Admin/Controller/ClientAjaxController.php

Public (and authenticated) AJAX handlers for client‑side enrichments (PSR-4 controller).

  • Registers:
    • wp_ajax[_nopriv]_wpvt_update_browser — updates latest visit's browser_type if previously chrome via VisitRepository.
    • wp_ajax[_nopriv]_wpvt_set_screen_resolution — updates latest visit's screen_resolution via VisitRepository (no‑op if unchanged).

src/Assets/pages/dashboard-page.php

Admin dashboard page rendering a summarized recent activity table.

  • Behavior
    • Queries aggregated recent visits (grouped by session_id, ip_address, user_agent), limited to 50 groups ordered by last activity.
    • Uses UserAgentParser::getBrowserDisplay() for browser name/class.
    • Outputs a table with: ID, time, IP, browser, type (human/bot), visits total, pages visited, language, resolution.
    • Includes a “Clear Table” button with id clear-table.

src/Assets/js/admin.js

Admin page interactions (jQuery).

  • Behavior
    • Binds click to #clear-table and performs AJAX POST to wpvt_clear_visits with wpvt_ajax.nonce.
    • On success, clears table body and shows a transient message.

src/Assets/js/frontend-collector.js

Universal front‑end collector for browser type (incl. Brave) and screen resolution.

  • Globals/Interfaces
    • Relies on localized wpvt_ajax object: { url, nonce }.
  • Classes
    • BrowserDetector — UA‑based detection with async Brave checks (navigator.brave, Brave wallet, feature probes, ad blocking heuristic). Methods: detect(), detectByUserAgent(), isBrave(), checkBraveAPI(), checkAdBlocking().
    • ScreenDataCollector — collects screen width/height (CSS pixels), stores in cookie/localStorage, sends via AJAX. Methods: collect(), sendViaAjax(resolution).
    • DataSender — static sendBrowserType(browserType) for posting Brave type updates.
    • UniversalCollector — orchestrates init: collects screen data, detects browser, posts Brave type, sets up resize tracking. Methods: init(), setupResizeTracking().
  • Lifecycle
    • documentReady() bootstraps UniversalCollector after DOM is ready.

src/Assets/css/admin.css

Styling for the admin dashboard table and controls.

  • Defines variables and layout for header, table, buttons; responsive tweaks for tablet/phone.
  • Utility classes: .bot-type, .human-type, column widths, hover states, and truncation behavior.

Global Hooks Summary

  • Activation: register_activation_hookActivation::activate()
  • Deactivation: register_deactivation_hookDeactivation::deactivate()
  • Uninstall: register_uninstall_hookDeactivation::uninstall()
  • Runtime
    • plugins_loaded → bootstrap main class
    • template_redirect (priority 99) → tracking entry point (after canonical redirects)
    • parse_request → optional early bot blocking
    • shutdown → session cleanup
    • wp_enqueue_scripts → enqueue front‑end collector
    • admin_menu, admin_init, admin_enqueue_scripts → admin UI
    • AJAX
      • wp_ajax_wpvt_clear_visits
      • wp_ajax_wpvt_update_browser, wp_ajax_nopriv_wpvt_update_browser
      • wp_ajax_wpvt_set_screen_resolution, wp_ajax_nopriv_wpvt_set_screen_resolution

Security & Data Notes (quick reference)

  • Nonces: wpvt_admin_nonce (admin actions), wpvt_ajax_nonce (public enrichment AJAX).
  • DB access: $wpdb->prepare() used for dynamic SQL; dbDelta() for schema.
  • Sanitization: sanitize_text_field, esc_url_raw on writes; esc_html/esc_attr on output.
  • Sessions: short‑lived DB sessions keyed by ip_address + user_agent; visit counters maintained per (session_id, url) unique pair.

Development Tips

  • Text domain: wrap new UI strings with __(), esc_html__(), etc., using wpvt.
  • When adding new AJAX handlers, always check_ajax_referer and scope updates to the current visitor/session.
  • For DB changes, prefer dbDelta() and keep indexes aligned with query patterns.

Admin JS modules (no central init)

  • There is no central core/init.js in admin. Feature scripts self-initialize on DOM ready.
  • Modules are idempotent and use namespaced events with an "off-before-on" pattern to avoid duplicate bindings.
  • Scripts are enqueued conditionally on plugin admin pages.

Privacy & Data Collection

The plugin stores minimal technical analytics in its own tables:

Collected fields per visit/session:

  • IP address (can be anonymized via filter)
  • User-Agent (trimmed to 500 chars)
  • URL (visited resource)
  • Referer (if valid URL)
  • Screen resolution (if JS collector sends it)
  • Browser language (2-letter code)
  • Browser type (edge / firefox / chrome / opera / safari / unknown / brave via JS)
  • Visitor type (human|bot — naive UA heuristic)

Not collected:

  • POST bodies
  • Form submissions
  • Cookies besides the session id (wpvt_sid) and optional screen resolution helper cookie
  • Logged-in admin navigation (skipped)

No automatic deletion/retention policy is enforced (for forensic transparency). Admin can manually purge.

Anonymizing IP Addresses

Enable a filter to obfuscate stored IPs (example: zero last octet for IPv4, collapse IPv6):

// In theme's functions.php or a small mu-plugin
add_filter('wpvt_anonymize_ip', '__return_true');

add_filter('wpvt_resolved_client_ip', function($ip){
  if (!apply_filters('wpvt_anonymize_ip', false)) return $ip;
  if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
    $parts = explode('.', $ip);
    $ip = $parts[0] . '.' . $parts[1] . '.' . $parts[2] . '.0';
  } elseif (strpos($ip, ':') !== false) {
    $blocks = explode(':', $ip);
    $ip = implode(':', array_slice($blocks, 0, 3)) . '::';
  }
  return $ip;
}, 20);

Alternative irreversible hash (keeps uniqueness, drops reversibility):

add_filter('wpvt_resolved_client_ip', function($ip){
  if (!apply_filters('wpvt_anonymize_ip', false)) return $ip;
  return hash('sha256', $ip . NONCE_SALT);
}, 30);

Adjust Log Verbosity

Default production level is info. Reduce noise further:

add_filter('wpvt_log_min_level', fn()=> 'warning');

Temporarily enable deep diagnostics:

add_filter('wpvt_log_min_level', fn()=> 'debug');

Purging Old Logs (Programmatic)

You can run a manual purge (e.g. via WP-CLI command or custom cron):

global $wpdb; $days = 30;
$table = $wpdb->prefix . 'wpvt_system_logs';
$wpdb->query($wpdb->prepare("DELETE FROM {$table} WHERE created_at < DATE_SUB(UTC_TIMESTAMP(), INTERVAL %d DAY)", $days));

Preserve Data on Uninstall

By default uninstall drops all plugin tables. To keep data:

// wp-config.php or a small mu-plugin
define('WPVT_PRESERVE_DATA', true);

// OR set the option manually (e.g. via wp shell / db):
update_option('wpvt_preserve_data_on_uninstall', true);

// OR use a filter:
add_filter('wpvt_preserve_data_on_uninstall', '__return_true');

When preservation is enabled, uninstall exits early and leaves tables & options intact.


License

Licensed under GPL-2.0-or-later. See LICENSE file.