cohorly/cohorly-php

Official Cohorly PHP SDK - server-side event tracking for hosted product analytics

Maintainers

Package info

github.com/cohorly-io/cohorly-php

pkg:composer/cohorly/cohorly-php

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-08-10 18:17 UTC

This package is auto-updated.

Last update: 2026-08-10 18:18:14 UTC


README

Server-side PHP SDK for Cohorly, the hosted product analytics platform. The API mirrors the Mixpanel PHP library, so if you have used mixpanel-php you already know this SDK.

Documentation: PHP SDK reference · Quickstart · HTTP API

Requires PHP >= 8.1 with ext-curl and ext-json. No other dependencies.

Installation

composer require cohorly/cohorly-php

Or add it to composer.json:

{
    "require": {
        "cohorly/cohorly-php": "^0.1"
    }
}

Quickstart

<?php
require 'vendor/autoload.php';

use Cohorly\Cohorly;

// Get the Cohorly class instance for your project token.
$cohorly = Cohorly::getInstance('YOUR_PROJECT_TOKEN', [
    'host' => 'https://cohorly-service.velloalabs.com', // your Cohorly server
]);

// Track an event.
$cohorly->track('button clicked', [
    'distinct_id' => 'user-123',
    'label' => 'sign-up',
]);

Events are queued in memory and sent in batches: when the queue reaches max_batch_size, when you call flush(), or automatically when the instance is destroyed at the end of the request.

Identifying users

// Set the distinct_id for every subsequent event in this request.
$cohorly->identify('user-123');
$cohorly->track('page viewed', ['page' => '/pricing']);

// Register super properties, merged into every event.
$cohorly->register('plan', 'pro');
$cohorly->registerAll(['region' => 'eu', 'beta' => true]);
$cohorly->unregister('beta');

// Link a new id to an existing one (e.g. after signup).
$cohorly->alias('user-123', 'anon-abc');

// Clear identity + super properties.
$cohorly->reset();

Every event is stamped with Cohorly's default properties (call-site properties always win):

Property Value
distinct_id from identify() or the call site
time unix milliseconds, defaults to now
$insert_id uuid v4, used for server-side dedup
$lib "php"
$lib_version SDK version

Managing user profiles

Profile operations live on $cohorly->people and map to Cohorly /engage operations:

// Set properties on a profile (overwrites existing values).
$cohorly->people->set('user-123', ['$name' => 'Ada', 'plan' => 'pro']);

// Set properties only if they are not already set.
$cohorly->people->setOnce('user-123', ['first_seen' => '2026-07-04']);

// Increment / decrement numeric properties.
$cohorly->people->increment('user-123', 'logins');       // +1
$cohorly->people->increment('user-123', 'credits', -5);  // -5

// Remove properties from a profile.
// NOTE: remove/unset/deleteUser are destructive and refused by the server on
// the project token alone (they need an org-owner or superadmin Authorization
// credential this SDK does not send) - server-side no-ops here; use the
// dashboard or the admin privacy API instead.
$cohorly->people->remove('user-123', ['plan']);
$cohorly->people->unset('user-123', ['plan']);  // alias of remove()

// Delete the profile entirely.
$cohorly->people->deleteUser('user-123');

Feature flags

Flags are evaluated server-side on every call - there is no cache, so a change in the dashboard takes effect on the next call:

if ($cohorly->flags->isFeatureEnabled('new-checkout', 'user-123')) {
    // ...
}

$cohorly->flags->getFeatureFlag('pricing-test', 'user-123');        // "variant-b" or a bool
$cohorly->flags->getFeatureFlagPayload('pricing-test', 'user-123'); // variant payload or null
$cohorly->flags->getAllFlags('user-123');                           // [key => [enabled, variant, payload, reason]]

Unknown flags read as false / null. Like the producers, a failed request never throws: an outage or an invalid token degrades to those same defaults (set debug to log the reason).

Flushing

$cohorly->flush(); // deliver all queued events, profile ops and aliases now

Any messages still queued when the instance goes out of scope are flushed by the destructor, like mixpanel-php.

Options

Pass options as the second argument to getInstance() / the constructor:

Option Default Description
host https://cohorly-service.velloalabs.com Cohorly server base URL
max_batch_size 50 messages per request (capped at the server's 500/batch limit)
consumer "curl" consumer strategy name
consumers [] map of extra strategy name => class
debug false log transport errors via error_log
timeout 30 request timeout (seconds)
connect_timeout 5 connection timeout (seconds)

Delivery and retries

The SDK implements the shared Cohorly SDK retry contract:

  • 429 / 5xx / network errors keep the queue and back off exponentially (base 2s, doubling, capped at 10 minutes, +/-20% jitter). A Retry-After header is honored (capped at 10 minutes).
  • 413 halves the flush batch size (floor 1) and retries.
  • 400 drops the offending batch permanently.
  • 401 (invalid token) keeps the queue with maximum backoff.
  • The queue holds at most 1000 messages; the oldest are dropped first.

PHP processes are request-scoped: the backoff deadline is respected within the process lifetime, and anything still queued is retried on flush() or in the destructor.

Custom consumers

Like mixpanel-php, delivery is pluggable. Extend Cohorly\ConsumerStrategies\AbstractConsumer, implement persist(array $batch): Response, and register it:

$cohorly = Cohorly::getInstance('TOKEN', [
    'consumer' => 'my_consumer',
    'consumers' => ['my_consumer' => MyConsumer::class],
]);

Development

composer install
vendor/bin/phpunit

The test suite uses a mock consumer; no network or Cohorly server needed.