Skip to content

klimeapp/klime-php

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

klime-php

Official Klime SDK for PHP.

Installation

composer require klime/klime

Quick Start

<?php

require_once 'vendor/autoload.php';

$client = new \Klime\Client([
    'writeKey' => 'your-write-key'
]);

// Identify a user
$client->identify('user_123', [
    'email' => 'user@example.com',
    'name' => 'Stefan'
]);

// Track an event
$client->track('Button Clicked', [
    'button_name' => 'Sign up',
    'plan' => 'pro'
], [
    'userId' => 'user_123'
]);

// Associate user with a group and set group traits
$client->group('org_456', [
    'name' => 'Acme Inc',
    'plan' => 'enterprise'
], [
    'userId' => 'user_123'
]);

// Shutdown gracefully (also called automatically on script end)
$client->shutdown();

Installation Prompt

Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:

Integrate Klime for customer analytics. Klime tracks user activity to identify which customers are healthy vs at risk of churning.

ANALYTICS MODES (determine which applies):
- Companies & Teams: Your customers are companies with multiple team members (SaaS, enterprise tools)
  → Use identify() + group() + track()
- Individual Customers: Your customers are individuals with private accounts (consumer apps, creator tools)
  → Use identify() + track() only (no group() needed)

KEY CONCEPTS:
- Every track() call requires either userId OR groupId (no anonymous events)
- Use groupId alone for org-level events (webhooks, cron jobs, system metrics)
- group() links a user to a company AND sets company traits (only for Companies & Teams mode)
- Order doesn't matter - events before identify/group still get attributed correctly

BEST PRACTICES:
- Initialize client ONCE (singleton or service container)
- Store write key in KLIME_WRITE_KEY environment variable
- shutdown() auto-called on script end, but call explicitly for long-running processes

Install: composer require klime/klime

$client = new \Klime\Client(['writeKey' => getenv('KLIME_WRITE_KEY')]);

// Identify users at signup/login:
$client->identify('usr_abc123', ['email' => 'jane@acme.com', 'name' => 'Jane Smith']);

// Track key activities:
$client->track('Report Generated', ['report_type' => 'revenue'], ['userId' => 'usr_abc123']);
$client->track('Feature Used', ['feature' => 'export', 'format' => 'csv'], ['userId' => 'usr_abc123']);
$client->track('Teammate Invited', ['role' => 'member'], ['userId' => 'usr_abc123']);

// If Companies & Teams mode: link user to their company and set company traits
$client->group('org_456', ['name' => 'Acme Inc', 'plan' => 'enterprise'], ['userId' => 'usr_abc123']);

INTEGRATION WORKFLOW:

Phase 1: Discover
Explore the codebase to understand:
1. What framework is used? (Laravel, Symfony, plain PHP, etc.)
2. Where is user identity available? (e.g., Auth::id(), $request->user()->id, $this->getUser()->getId(), $_SESSION)
3. Is this Companies & Teams or Individual Customers?
   - Look for: organization, workspace, tenant, team, account models → Companies & Teams (use group())
   - No company/org concept, just individual users → Individual Customers (skip group())
4. Where do core user actions happen? (controllers, services, event listeners, commands)
5. Is there existing analytics? (search: segment, posthog, mixpanel, amplitude, track)
Match your integration style to the framework's conventions.

Phase 2: Instrument
Add these calls using idiomatic patterns for the framework:
- Initialize client once (Laravel: service provider, Symfony: service definition, plain PHP: bootstrap)
- identify() in auth/login success handler
- group() when user-org association is established (Companies & Teams mode only)
- track() for key user actions (see below)

WHAT TO TRACK:
Active engagement (primary): feature usage, resource creation, collaboration, completing flows
Session signals (secondary): login/session start, dashboard access - distinguishes "low usage" from "churned"
Do NOT track: every request, health checks, middleware passthrough, queued jobs

Phase 3: Verify
Confirm: client initialized, shutdown handled, identify/group/track calls added

Phase 4: Summarize
Report what you added:
- Files modified and what was added to each
- Events being tracked (list event names and what triggers them)
- How userId is obtained (and groupId if Companies & Teams mode)
- Any assumptions made or questions

API Reference

Constructor

$client = new \Klime\Client([
    'writeKey' => 'your-write-key',        // Required: Your Klime write key
    'endpoint' => 'https://i.klime.com',   // Optional: API endpoint
    'flushInterval' => 2000,               // Optional: Milliseconds between flushes
    'maxBatchSize' => 20,                  // Optional: Max events per batch (max: 100)
    'maxQueueSize' => 1000,                // Optional: Max queued events
    'retryMaxAttempts' => 5,               // Optional: Max retry attempts
    'retryInitialDelay' => 1000,           // Optional: Initial retry delay in ms
    'flushOnShutdown' => true,             // Optional: Auto-flush on script end
    'logger' => function($level, $msg, $ctx) { ... },  // Optional: Logging callback
    'onError' => function($error, $events) { ... },    // Optional: Error callback
    'onSuccess' => function($response) { ... },        // Optional: Success callback
]);

Methods

track(string $event, ?array $properties = null, ?array $options = null)

Track an event. Events can be attributed in two ways:

  • User events: Provide userId to track user activity (most common)
  • Group events: Provide groupId without userId for organization-level events
// User event (most common)
$client->track('Button Clicked', [
    'button_name' => 'Sign up',
    'plan' => 'pro'
], [
    'userId' => 'user_123'
]);

// Group event (for webhooks, cron jobs, system events)
$client->track('Events Received', [
    'count' => 100,
    'source' => 'webhook'
], [
    'groupId' => 'org_456'
]);

// User event with explicit group context
$client->track('Feature Used', [
    'feature' => 'export'
], [
    'userId' => 'user_123',
    'groupId' => 'org_456'
]);

identify(string $userId, ?array $traits = null, ?array $options = null)

Identify a user with traits.

$client->identify('user_123', [
    'email' => 'user@example.com',
    'name' => 'Stefan',
    'plan' => 'pro'
]);

group(string $groupId, ?array $traits = null, ?array $options = null)

Associate a user with a group and/or set group traits.

// Associate user with a group and set group traits (most common)
$client->group('org_456', [
    'name' => 'Acme Inc',
    'plan' => 'enterprise'
], [
    'userId' => 'user_123'
]);

// Just link a user to a group (traits already set)
$client->group('org_456', null, ['userId' => 'user_123']);

// Just update group traits (e.g., from a webhook)
$client->group('org_456', [
    'plan' => 'enterprise',
    'employee_count' => 50
]);

flush()

Manually flush queued events immediately.

$client->flush();

shutdown()

Gracefully shutdown the client, flushing remaining events.

$client->shutdown();

getQueueSize(): int

Return the number of events currently in the queue.

$pending = $client->getQueueSize();
echo "{$pending} events waiting to be sent";

Synchronous Methods

For cases where you need confirmation that events were sent (e.g., in tests, before exit), use the synchronous variants:

trackSync(string $event, ?array $properties, ?array $options): BatchResponse

Track an event synchronously. Returns BatchResponse or throws SendException.

use Klime\SendException;

try {
    $response = $client->trackSync('Critical Action', ['key' => 'value'], ['userId' => 'user_123']);
    echo "Sent! Accepted: {$response->accepted}";
} catch (SendException $e) {
    echo "Failed to send: {$e->getMessage()}";
}

identifySync(string $userId, ?array $traits): BatchResponse

Identify a user synchronously. Returns BatchResponse or throws SendException.

groupSync(string $groupId, ?array $traits, ?array $options): BatchResponse

Associate a user with a group synchronously. Returns BatchResponse or throws SendException.

Features

  • Automatic Batching: Events are automatically batched and sent every 2 seconds or when the batch reaches 20 events
  • Automatic Retries: Failed requests are automatically retried with exponential backoff
  • Process Exit Handling: Automatically flushes events on script end via register_shutdown_function
  • Zero Dependencies: Uses only PHP standard library (curl, json)

Performance

Tracking methods are designed for minimal overhead in PHP's request-response model. When you call track(), identify(), or group(), the SDK:

  1. Adds the event to an in-memory array (microseconds)
  2. Returns immediately

Events are flushed and sent to Klime's servers:

  • Automatically at script end: via register_shutdown_function() - no explicit call needed
  • When batch is full: if 20+ events queue up, they're sent immediately
  • On explicit flush(): for long-running processes
// This adds to the queue - no HTTP request yet
$client->track('Button Clicked', ['button' => 'signup'], ['userId' => 'user_123']);

// Your code continues without waiting
return response()->json(['success' => true]);

// Events are automatically flushed when the PHP script ends

For typical web requests, the HTTP request to Klime happens after your response is sent to the user (during PHP's shutdown phase), so it doesn't add latency to your application's response time.

For long-running processes (queue workers, daemons), call flush() periodically or when appropriate to send queued events.

Configuration

Default Values

Setting Default
flushInterval 2000ms
maxBatchSize 20 events
maxQueueSize 1000 events
retryMaxAttempts 5 attempts
retryInitialDelay 1000ms
flushOnShutdown true

Logging

The SDK accepts a callable for logging (no PSR-3 dependency required):

$client = new \Klime\Client([
    'writeKey' => 'your-write-key',
    'logger' => function(string $level, string $message, array $context): void {
        error_log("[Klime][$level] $message " . json_encode($context));
    },
]);

// Or wrap a PSR-3 logger
$client = new \Klime\Client([
    'writeKey' => 'your-write-key',
    'logger' => fn($level, $msg, $ctx) => $psrLogger->log($level, $msg, $ctx),
]);

Callbacks

$client = new \Klime\Client([
    'writeKey' => 'your-write-key',
    'onError' => function(\Throwable $error, array $events): void {
        // Report to your error tracking service
        \Sentry\captureException($error);
        error_log("Failed to send " . count($events) . " events: " . $error->getMessage());
    },
    'onSuccess' => function(\Klime\BatchResponse $response): void {
        error_log("Sent {$response->accepted} events");
    },
]);

Error Handling

The SDK automatically handles:

  • Transient errors (429, 503, network failures): Retries with exponential backoff
  • Permanent errors (400, 401): Logs error and drops batch
  • Rate limiting: Respects Retry-After header

For synchronous operations, use *Sync() methods which throw SendException on failure:

use Klime\SendException;

try {
    $response = $client->trackSync('Event', [], ['userId' => 'user_123']);
} catch (SendException $e) {
    echo "Failed: {$e->getMessage()}, events: " . count($e->events);
}

Size Limits

  • Maximum event size: 200KB
  • Maximum batch size: 10MB
  • Maximum events per batch: 100

Events exceeding these limits are rejected and logged.

Laravel Example

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Klime\Client;

class AnalyticsController extends Controller
{
    private Client $klime;

    public function __construct()
    {
        $this->klime = new Client([
            'writeKey' => config('services.klime.write_key')
        ]);
    }

    public function buttonClicked(Request $request)
    {
        $this->klime->track('Button Clicked', [
            'button_name' => $request->input('buttonName')
        ], [
            'userId' => (string) $request->user()->id
        ]);

        return response()->json(['success' => true]);
    }
}

Symfony Example

<?php

namespace App\Controller;

use Klime\Client;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;

class AnalyticsController extends AbstractController
{
    private Client $klime;

    public function __construct()
    {
        $this->klime = new Client([
            'writeKey' => $_ENV['KLIME_WRITE_KEY']
        ]);
    }

    public function buttonClicked(Request $request): JsonResponse
    {
        $data = json_decode($request->getContent(), true);

        $this->klime->track('Button Clicked', [
            'button_name' => $data['buttonName'] ?? null
        ], [
            'userId' => (string) $this->getUser()->getId()
        ]);

        return $this->json(['success' => true]);
    }
}

Plain PHP Example

<?php

require_once 'vendor/autoload.php';

$client = new \Klime\Client([
    'writeKey' => getenv('KLIME_WRITE_KEY')
]);

// Handle POST request
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);

    $client->track('Button Clicked', [
        'button_name' => $data['buttonName'] ?? null
    ], [
        'userId' => $data['userId'] ?? null
    ]);

    header('Content-Type: application/json');
    echo json_encode(['success' => true]);
}

Requirements

  • PHP 8.1 or higher
  • ext-curl
  • ext-json

License

MIT

About

Klime SDK for PHP

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages