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.

★ 0stars

Install

Download source zip No release zip yet

This is the repository archive. It installs, but the folder name will carry the branch suffix and updates will not flow.

Install straight from GitHub with WP-CLI:

wp plugin install https://github.com/avniloff/wp-visitors-tracker/archive/refs/heads/main.zip
No built-in updater. New versions won't appear under Dashboard → Updates. Install new releases yourself, or let Git Updater track the repository.

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(): void — wp_cache_flush().
      • private static function logDeactivation(): void — updates timestamp and logs in debug mode.
      • private static function dropTables(): void — DROP 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(): string — bin2hex(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_hook → Activation::activate()
  • Deactivation: register_deactivation_hook → Deactivation::deactivate()
  • Uninstall: register_uninstall_hook → Deactivation::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.