Skip to main content
traffical/sdk is the official server-side SDK for PHP (^8.1). It fetches the config bundle, caches it (shared across PHP-FPM workers via a PSR-16 store), resolves parameters locally per request, and flushes events after the response is returned so experimentation never adds latency to the user-visible request. It shares the language-agnostic Traffical SDK spec with the TypeScript and Swift SDKs — the same SHA-256 v2 bucketing, the same layered resolution engine, the same contextual-bandit scoring — so a given unit buckets identically on every platform.

Installation

You also need a PSR-18 HTTP client and PSR-17 factories. Any compliant implementation works; the SDK auto-discovers them via php-http/discovery:

Initialization

ClientOptions is an immutable value object. Construct it with named arguments, or refine an existing instance with the fluent with*() methods (each returns a new instance).

Options

Resolving parameters

Context

The context array is used for:
  • Bucketing — the unit key (typically userId, as configured on the project) drives which allocation the user gets.
  • Targeting — every field is available for policy condition evaluation. A policy with the condition plan in ["pro", "enterprise"] only applies when context['plan'] is one of those values.

Defaults

Always pass defaults for every parameter you read. They’re the fallback when no policy matches, when the bundle isn’t loaded yet, or when Traffical is unreachable — and they make your code’s intent explicit even when there’s no active experiment.

Decisions vs getParams

getParams returns just the resolved values. decide returns a DecisionResult (assignments + a decisionId + metadata) — useful when you want to record an exposure only when the user actually sees the treatment, or pass the decision ID downstream.
See decisions & attribution for when this matters.

Tracking events

track(string $event, ?array $properties = null, ?TrackOptions $options = null) — the event name first, an optional properties array second, and a TrackOptions bag third (decisionId, unitKey, value, values, eventTimestamp):
If you’re running an adaptive policy, the optimizer uses these events as reward signals to learn which variants perform best.

Server mode

Delegate resolution to Traffical’s edge instead of evaluating a local bundle. Each getParams()/decide() performs (and caches per request) a POST /v1/resolve. Use it when caching a full bundle isn’t reasonable or you want zero client-side evaluation logic.

PHP lifecycle & event flushing

PHP’s request/response model differs from a long-lived Node process. On construction the Client registers a shutdown handler that:
  1. Calls fastcgi_finish_request() if available, returning the buffered HTTP response to the client immediately.
  2. Flushes queued decision/exposure/track events over PSR-18 HTTP — fire-and-forget, so transport errors are caught and logged via PSR-3 and never surface to the user.
Events also flush automatically once the in-memory queue reaches batchSize (default 10).

CLI, queue workers, and long-running processes

fastcgi_finish_request() doesn’t exist under the CLI SAPI or in long-lived workers. Flush explicitly at safe checkpoints:
Or call $client->close() at shutdown — the single teardown verb, which runs plugin teardown (onDestroy) hooks and awaits a final flush.

Sharing config across FPM workers

Each FPM worker is a separate process. Inject a PSR-16 shared cache (e.g. symfony/cache with APCu/Redis) so workers share one bundle rather than each refetching on its first request:
The cached config source keys the bundle by projectId:env and refreshes lazily on refreshIntervalMs. The same shared cache can back cross-request assignment-logger deduplication.

BYO warehouse-native assignment logging

Route structured assignment rows through your own pipeline (Segment, RudderStack, a DB, a queue) so assignment data never leaves your infrastructure. The WarehouseNativeLogger helper maps each entry to a snake_case row, including the stable policy_key/allocation_key used for warehouse joins:
See warehouse-native metrics for how assignment rows join to your metrics.

Plugins

Hook into the SDK lifecycle (onBeforeDecision, onDecision, onExposure, onTrack, onDestroy). Built-ins: DebugPlugin, DecisionTrackingPlugin, WarehouseNativeLoggerPlugin.

Framework integrations

Laravel

Auto-discovered TrafficalServiceProvider + Traffical facade.

Symfony

TrafficalBundle with a traffical config tree.

OpenFeature

Optional TrafficalProvider (requires open-feature/sdk).

Error handling

The SDK never throws during resolution. If the bundle isn’t loaded yet — or never loaded successfully — getParams returns your defaults and decide returns the default assignments. Transport and refresh errors are caught and logged via the injected PSR-3 logger.

Next steps

A/B testing

Run a static experiment.

Warehouse-native

Compute metrics from your own data.

Canonical experiments

Patterns for backend, batch, and cross-surface tests.

API reference

The endpoints the SDK calls.