Storage

This file implements the Storage Abstraction Layer for PriviMetrics.
It allows the system to store and retrieve analytics data using either:

  • MySQL (database mode)

  • XML files (filesystem mode)

The rest of the application uses a single interface, regardless of the chosen storage backend.


1. Purpose

StorageManager provides:

  • Unified API for saving analytics data

  • Unified API for loading analytics data

  • Automatic MySQL connection handling

  • Backward-compatible XML storage

This design allows PriviMetrics to run both:

  • in lightweight file-based mode (no database)

  • and in scalable database mode

without changing the tracking or dashboard code.


2. Class Overview

class StorageManager {
    private $config;
    private $pdo = null;
}

Properties

Property Description
$config System configuration (database credentials, data directory, etc.)
$pdo Lazy-loaded PDO MySQL connection

3. Database Connection

getConnection()

Creates and returns a PDO MySQL connection when first needed.

Features:

  • UTF-8 (utf8mb4) support

  • Prepared statements

  • Exception-based error handling

  • Lazy initialization (connection only created when used)

If the connection fails:

  • an error is written to the server logs

  • a generic exception is thrown to avoid leaking credentials


4. Public API

These two methods are used by the rest of the system.

saveTracking($storageType, $siteData, $trackingData)

Stores a single tracking event.

Parameter Description
$storageType "mysql" or "xml"
$siteData Information about the tracked website
$trackingData Visitor, page, and tracking metadata

Routing:

  • If "mysql"saveToMySQL()

  • Otherwise → saveToXML()


loadAnalytics($storageType, $siteData, $dateRange)

Loads analytics data for the dashboard.

Parameter Description
$storageType "mysql" or "xml"
$siteData Website being queried
$dateRange UNIX timestamps: start and end

Routing:

  • If "mysql"loadFromMySQL()

  • Otherwise → loadFromXML()

Returns a normalized array of analytics rows, regardless of backend.


5. XML Storage Engine

XML mode is designed for lightweight installations without a database.

File layout

/data/
   /example-site/
      2025-01-10.xml
      2025-01-11.xml

Each file contains:

  • visitors (<visit>)

  • each visitor has multiple page views (<entry>)

A .lock file is used to prevent concurrent write corruption.


saveToXML()

  • Sanitizes the site name for directory creation

  • Creates a daily XML file

  • Uses file locking for safe concurrent writes

  • Tracks visitors using user_hash

  • Appends page views to existing visitors or creates new ones

Stored fields include:

  • timestamp

  • date

  • hour

  • page URL and title

  • referrer

  • search query

  • country and IP


loadFromXML()

  • Iterates through daily XML files

  • Filters by date range

  • Converts XML entries into a normalized PHP array

Each returned row includes:

  • page_url

  • page_title

  • referrer

  • search_query

  • country

  • country_code

  • hour

  • date

This allows the dashboard to work exactly the same as with MySQL.


6. MySQL Storage Engine

MySQL mode uses a single-table schema called analytics.

Each page view is stored as one row.


saveToMySQL()

Inserts one analytics event into the analytics table.

Stored fields:

  • unique ID

  • site ID

  • IP address

  • country and country code

  • user agent

  • user hash

  • timestamp (UNIX)

  • date

  • hour

  • page URL

  • page title

  • referrer

  • search query

Uses:

  • prepared statements

  • UTC timestamps

  • cryptographically strong unique IDs

If insertion fails:

  • the error is logged

  • the function returns false


loadFromMySQL()

Fetches analytics data for a site and time range.

Query logic:

  • filters by site_id

  • filters by timestamp range

  • sorts by newest first

Returns the same normalized structure as XML mode:

  • page_url

  • page_title

  • referrer

  • search_query

  • country

  • country_code

  • hour

  • date

This guarantees that the dashboard logic does not care which backend is used.


7. Design Benefits

This architecture provides:

  • Seamless switching between XML and MySQL

  • Easy upgrades from file-based to database-based storage

  • High performance in MySQL mode

  • Zero-database installations in XML mode

  • A single, stable API for the rest of the system