MenuPHP

Proxy guides

PHP

Serve the ProductLense script and forward events with two small PHP files, for shared hosting and any PHP site without a framework.

Two files cover a plain PHP site, including shared hosting where you cannot change the web server's configuration. The first serves the script from your domain on any plan; the second forwards events on the Scale plan. Read the proxy overview first for what each one fixes.

Serve the script

The file caches the script for an hour in the system temp directory, so most page loads never call ProductLense.

js/a.php
<?php
$cache = sys_get_temp_dir() . '/productlense-a.js';
if (!is_file($cache) || filemtime($cache) < time() - 3600) {
    $js = @file_get_contents('https://api.productlense.app/a.js');
    if ($js !== false && $js !== '') {
        file_put_contents($cache, $js, LOCK_EX);
    }
}
if (!is_file($cache)) {
    http_response_code(502);
    exit;
}
header('Content-Type: application/javascript; charset=utf-8');
header('Cache-Control: public, max-age=86400');
readfile($cache);
HTML
<script defer data-site="SITE_ID" data-api="https://api.productlense.app" src="/js/a.php"></script>

Forward events (Scale plan)

Store an API key with the events scope in the PL_API_KEY environment variable (or a config file outside the web root), and create this file so it answers at /m/api/e:

m/api/e/index.php
<?php
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit;
}
$raw = file_get_contents('php://input', false, null, 0, 8193);
if ($raw === false || strlen($raw) > 8192) {
    http_response_code(413);
    exit;
}
$body = json_decode($raw, true);
if (!is_array($body)) {
    http_response_code(400);
    exit;
}
// REMOTE_ADDR is the visitor unless you sit behind a proxy or CDN; then use its client IP header.
$extra = ['ip' => $_SERVER['REMOTE_ADDR'] ?? '', 'ua' => $_SERVER['HTTP_USER_AGENT'] ?? ''];
if (isset($body['b']) && is_array($body['b'])) {
    $body['b'] = array_map(fn ($e) => is_array($e) ? array_merge($e, $extra) : $e, $body['b']);
} else {
    $body = array_merge($body, $extra);
}
$ch = curl_init('https://api.productlense.app/api/v1/events');
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => json_encode($body),
    CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('PL_API_KEY'), 'Content-Type: application/json'],
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT => 5,
]);
curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
http_response_code($status ?: 502);

Then set data-api="https://example.com/m" on the tag.

Check it works

  1. Open https://example.com/js/a.php: it returns JavaScript.
  2. Load a page with the developer tools open. The Network tab shows a POST to api/e with status 204.
  3. Open the Live page in the ProductLense dashboard. Your visit appears within a few seconds.

    ↑ ↓ to move, Enter to open