PHP SDK

Send events from any PHP server using a project API key. Works with Laravel, Symfony, WordPress, Slim, and anything else that runs PHP 8.1+ and can make outbound HTTPS calls.

clamp-sh/analytics-phpMIT

Paste to your agent

Wire Clamp into a PHP backend
Adds clamp/analytics, initializes it on app boot, and tracks the project's primary conversion event from the relevant handler.

Install

terminal
composer require clamp/analytics

PHP 8.1+ supported. Requires ext-curl and ext-json (both standard).

Use an API key that starts with sk_proj_ from Settings → API Keys. Keys are scoped to one project.

Quick start

bootstrap.php
use Clamp\Analytics\Analytics;
use Clamp\Analytics\Money;

Analytics::init(
    projectId: 'proj_xxx',
    apiKey: getenv('CLAMP_API_KEY'),
);

// Simple event
Analytics::track('signup', ['plan' => 'pro', 'method' => 'email']);

// Link a server event to a browser visitor
Analytics::track(
    'subscription_started',
    [
        'plan' => 'pro',
        'total' => new Money(29.00, 'USD'),
    ],
    anonymousId: 'aid_xxx',
);

API

Analytics::init(projectId, apiKey, endpoint = null)

Initializes the SDK. Call once at application bootstrap. Stores config in static state on the Analytics class.

Analytics::track(name, properties = [], anonymousId = null, timestamp = null)

Sends a server event. Returns true on success.

Throws ClampHttpException on a non-2xx response, ClampNotInitializedException if init()wasn't called.

new Money(amount, currency)

A typed monetary value. amount is in major units (29.00, not 2900). currency is an ISO 4217 code.

Framework integrations

Laravel

Initialize in a service provider so the SDK is ready before the first request.

app/Providers/AppServiceProvider.php
namespace App\Providers;

use Clamp\Analytics\Analytics;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Analytics::init(
            projectId: config('services.clamp.project_id'),
            apiKey: config('services.clamp.api_key'),
        );
    }
}
app/Http/Controllers/SignupController.php
use Clamp\Analytics\Analytics;

public function complete(Request $request)
{
    $user = $this->createUser($request);

    Analytics::track('signup', [
        'plan' => $user->plan,
        'method' => 'email',
    ], anonymousId: $request->cookie('clamp_aid'));

    return redirect('/welcome');
}

Symfony

Initialize on the kernel boot event.

src/EventSubscriber/ClampInitSubscriber.php
namespace App\EventSubscriber;

use Clamp\Analytics\Analytics;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\KernelEvent;

class ClampInitSubscriber implements EventSubscriberInterface
{
    private bool $initialized = false;

    public function __construct(
        private string $projectId,
        private string $apiKey,
    ) {}

    public function onKernelRequest(): void
    {
        if (!$this->initialized) {
            Analytics::init($this->projectId, $this->apiKey);
            $this->initialized = true;
        }
    }

    public static function getSubscribedEvents(): array
    {
        return [KernelEvent::class => 'onKernelRequest'];
    }
}

WordPress

Initialize on the plugins_loaded action.

my-plugin.php
require __DIR__ . '/vendor/autoload.php';

use Clamp\Analytics\Analytics;

add_action('plugins_loaded', function () {
    Analytics::init(
        projectId: getenv('CLAMP_PROJECT_ID'),
        apiKey: getenv('CLAMP_API_KEY'),
    );
});

add_action('user_register', function (int $user_id) {
    $user = get_userdata($user_id);
    Analytics::track('signup', [
        'method' => 'email',
        'role' => $user->roles[0] ?? 'subscriber',
    ]);
});

Capturing exceptions

Wrap unexpected exceptions and send them as $error events on the same pipeline as the rest of your tracking, queryable via the MCP errors.* tools.

WebhookController.php
use Clamp\Analytics\Analytics;

try {
    $this->processWebhook($payload);
} catch (\Throwable $e) {
    Analytics::captureError($e, ['webhook' => 'stripe', 'event' => $payload->type]);
    throw $e;
}

See Error capture for the wire shape, length caps, and the MCP read tools.

Errors and retries

The SDK is synchronous and throws on failure. There are no automatic retries. Wrap calls in try/catch if you want fire-and-forget:

safe_track.php
use Clamp\Analytics\Analytics;
use Clamp\Analytics\ClampException;

try {
    Analytics::track('subscription_started', [...]);
} catch (ClampException $e) {
    error_log('failed to send to Clamp: ' . $e->getMessage());
}

For high-throughput webhook handlers, defer the track() call to a background queue (Laravel queues, Symfony Messenger).

Linking browser and server events

Pass the anonymous ID from the browser to your API, then include it in server-side calls. See the linking pattern on the server-side docs page; the same pattern applies in PHP.