Pure PHP SDK
The core package: zero dependencies, compatible with PHP ≥ 7.4 (shared hosting, legacy projects and CMSs included). It is the foundation of the Laravel and Symfony bridges, and the tool for "unblockable" tracking on any PHP site.
Installation
composer require quiet-metrics/php-metrics
Usage
use QuietMetrics\Client; $qm = new Client('qm_pub_XXXXXXXXXXXXXXXXX', 'qm_sec_XXXXXXXXXXXXXXXX'); $qm->pageview(); // context inferred from the current request $qm->event('purchase', ['amount' => 49]); // custom event
The context (URL, referrer, IP, User-Agent, language) is inferred from the current request's superglobals, and every field can be overridden:
$qm->pageview(['url' => 'https://mysite.com/thank-you', 'referrer' => null]);
Constructor options
$qm = new Client('qm_pub_…', 'qm_sec_…', [ 'endpoint' => 'https://quietmetrics.dev/api/v1/collect', // default 'timeout_ms' => 400, // total send budget (min 50 ms) 'async' => true, // fire-and-forget socket; false = short cURL 'trust_proxy_headers' => false, // read X-Forwarded-For / -Proto (reverse proxy) 'defaults' => [], // fields merged into every hit ]);
defaults comes in handy for multi-site setups: instantiate one client per site with its key, or set a common fallback language/URL.
Signed mode (recommended)
With the secret key, every send carries the X-QM-Timestamp and X-QM-Signature headers (HMAC-SHA256 of "{timestamp}.{body}"). Only a valid signature allows the service to take into account the visitor's IP and browser passed in the payload; without it, your server itself would be counted as the sole unique visitor.
Two points to watch:
- Server clock. The signature is rejected beyond ±5 minutes of drift (anti-replay). An NTP-synchronized server never has to think about it; a container with a frozen clock does.
- The secret key must never appear browser-side or in a public repository.
From the CLI, a cron, a worker
Outside an HTTP request, there is no current URL or IP: without an explicit url, the send is silently dropped. Pass the context:
$qm->event('invoice-generated', ['amount' => 99], [ 'url' => 'https://mysite.com/invoices', 'ts' => time(), ]);
Shared hosting
The send first attempts an outbound socket (fsockopen, fire-and-forget, ~1 ms perceived). If your host disables it, it automatically falls back to cURL with a short request. If both are unavailable, the send is silently dropped: your site never breaks.
First-party proxy for the script
The examples/qm-proxy.php file relays browser hits through your domain, so domain-based blocklists only see your site:
- Drop
qm-proxy.phpand a copy ofqm.jsat the site root. - Fill in the
QM_ENDPOINTandQM_SECRETconstants in the file header (with the secret key, the visitor's IP and browser are taken into account). - Point the script at it:
<script defer src="/qm.js" data-site="qm_pub_…" data-endpoint="/qm-proxy.php"></script>
CSP-wise, script-src 'self' and connect-src 'self' are enough: no third-party domain left browser-side.
Payload limits
JSON payload ≤ 4 KB, event name ≤ 120 characters, ≤ 30 scalar properties (values truncated at 190 characters). Beyond that, the send is dropped or truncated service-side, never a visible error.
Robustness contract
Never breaks the host site: non-blocking send, automatic fallbacks, and any failure (network, DNS, platform unavailable) is silent by contract.
Can't find your answer?
Support replies on business days.