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