# 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](/docs/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.

```php title="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`:

```php title="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.
