alitycs/alitycs-sdk-php

Alitycs Analytics SDK for PHP — server-side event tracking with batching, sessions, and retry.

Maintainers

Package info

github.com/alitycs/alitycs-sdk-php

pkg:composer/alitycs/alitycs-sdk-php

Transparency log

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.1 2026-08-30 10:52 UTC

This package is auto-updated.

Last update: 2026-08-30 10:52:57 UTC


README

Alitycs Analytics SDK for PHP server applications. Events are batched and delivered to the Alitycs ingest endpoint over HTTPS, validating against the shared event contract (specs/event-schema.json v0.4.0).

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

composer require alitycs/alitycs-sdk-php

Quickstart

use Alitycs\Client;
use Alitycs\RevenuePayload;

$alitycs = new Client('pk_live_…', [
    'endpoint' => 'https://api.alitycs.com/events',
    'flushSize' => 20,
]);

$alitycs->identify('usr_1842', ['plan' => 'pro']);
$alitycs->track('signup_completed', ['plan' => 'free']);
$alitycs->page('Dashboard');
$alitycs->captureError('checkout_failed', ['code' => 'E_CARD']);
$alitycs->trackRevenue(RevenuePayload::transaction('fact_1', '19.99', 'USD'));
$alitycs->setGlobalProperties(['suite' => 'checkout']);
$alitycs->reset();          // rotates session + anonymous id, clears the user

$alitycs->shutdown();       // or let script shutdown deliver the remainder

There is no background flusher

PHP has no background threads, so this SDK has no timer-based flush. Queued events are delivered by exactly three triggers:

  1. Queue depth — when the queue reaches flushSize.
  2. Explicit flush() — sends everything currently queued as one batch.
  3. Script shutdown — via register_shutdown_function(), so a request that ends without an explicit flush still delivers.

shutdown() resolves ownership without silently losing queued events; it is safe to call more than once. After it returns, every enqueued event has been sent, durably retained, or permanently dropped and counted under the retry policy below. Events enqueued after shutdown() are ignored (logged in debug mode), because there is nothing left to deliver them.

flushInterval exists for API parity but is opportunistic: it is never a timer. It is checked only while events keep arriving — each enqueue compares elapsed wall time against the interval and flushes if it has elapsed. A request whose final event arrives before the interval does will not be sent until flush()/shutdown() or the next event. Set it to 0 to disable the check entirely.

In long-running processes (workers, daemons), call flush() on your own cadence — do not rely on request lifecycle hooks that never fire.

Long-running workers (Octane, Swoole, RoadRunner)

A single Client instance kept alive across many logical requests carries state: identify()'s user id, setGlobalProperties(), and the session/anonymous ids. Without a per-request reset that state leaks into the next request's events — event A's user gets credited for request B's actions.

Call resetForRequest() at the top of every logical request, before tracking anything:

// e.g. Laravel Octane: in a middleware or the ListenForEventsTick listener
$app->middleware(function ($request, $next) use ($alitycs) {
    $alitycs->resetForRequest();   // clears user id, global properties, session identity
    return $next($request);
});

When logical requests can overlap on one client, pass a named per-call userId instead of mutating shared identity. The override is captured only for that event:

$alitycs->track('checkout_started', userId: $request->userId);
$alitycs->captureError('checkout_failed', ['code' => 'E_CARD'], userId: $request->userId);

The same optional argument is available on trackRevenue() and page(). Omitting it uses the ambient identity from identify(); passing a blank string explicitly suppresses that ambient identity for the individual event.

Queued but unsent events are not dropped by resetForRequest(); flush beforehand if they should be delivered before the identities change. For Swoole coroutine workers, prefer per-call userId values; resetting shared ambient state while requests overlap is not safe.

Configuration

Option Default Meaning
endpoint https://api.alitycs.com/events Ingest URL
flushSize 25 Queue depth that triggers a send
flushInterval 10.0 Opportunistic seconds between flushes; 0 disables
maxQueueSize 1000 Buffered events before new arrivals are dropped
maxRetries 3 Attempts after the first (5xx / 429 / network errors only)
timeoutMs 10000 Per-request HTTP timeout
sessionTimeout 1800000 Inactivity (ms) before the session id rotates
debug false Log diagnostics to stderr under [Alitycs]
batching true When false, every event is its own single-event batch
persistencePath null Optional exact in-flight batch WAL path for restart recovery

Unknown option names throw — a typo fails loudly instead of running on defaults.

Retry policy

Batches are retried with exponential backoff (1s, 2s, 4s … capped at 10s) on 5xx, 429, and network errors, up to maxRetries times. Any other 4xx is permanent and is never retried. A server Retry-After value replaces the generated delay and uses a separate 60s safety bound rather than the 10s client-backoff cap. Delivery failures are logged, never thrown — a dead endpoint cannot fatal your application. Without persistence, an exhausted transient batch is lost. With persistencePath, the exact serialized batch is atomically retained. A later flush()/shutdown() replays it byte-identically after any remaining Retry-After pause has elapsed; it returns promptly while a pause is still active. Terminal responses acknowledge and remove the record. The WAL also accepts queued events during a blocked shutdown and is capped at maxQueueSize retained events. Configure one process owner per path. After a fork, the child detaches from the inherited parent WAL; create a fresh client with a child-specific persistencePath when child delivery must also be durable.

Revenue

trackRevenue() requires a secret key with revenue:write. Payloads are built through named constructors matching the wire contract's three variants: RevenuePayload::transaction(...), ::mrrSnapshot(...), and ::mrrBaselineComplete(...). Invalid combinations of fields for a variant cannot be constructed.

Not in scope

Feature flags, session recording, group analytics, and log ingestion are not Alitycs capabilities and are deliberately absent here. The PHP SDK is server-class: it has no autocapture — call page() explicitly.

Development

composer install
vendor/bin/phpunit                       # unit + integration suite
composer run test:coverage               # suite + coverage gate (lines >= 90%, methods >= 85%)
composer run conformance                 # conformance app against $CAPTURE_URL