PHPackages                             traffical/sdk - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. traffical/sdk

ActiveLibrary[Testing &amp; Quality](/categories/testing)

traffical/sdk
=============

Traffical PHP SDK — deterministic feature flags, experiments, and warehouse-native experimentation. Conformance-tested against the language-agnostic Traffical SDK spec.

v0.2.0(1w ago)01MITPHPPHP ^8.1CI passing

Since Jun 4Pushed 1w agoCompare

[ Source](https://github.com/traffical/php-sdk)[ Packagist](https://packagist.org/packages/traffical/sdk)[ Docs](https://traffical.io)[ RSS](/packages/traffical-sdk/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (26)Versions (4)Used By (0)

Traffical PHP SDK
=================

[](#traffical-php-sdk)

[Traffical](https://traffical.io) is one control plane for experiments, feature flags, and adaptive optimization. Instead of hard-coding decisions, you expose **typed parameters** — numbers, strings, booleans, and JSON, not just on/off toggles — and control behavior across web, mobile, push, and backend from a single place. Parameters are resolved **locally in the SDK** (sub-millisecond, no network round trips at runtime), and metrics are computed **warehouse-native**, against data you own. Start with a feature flag, graduate it to an A/B test, and let adaptive optimization shift traffic to the winning variant — all on the same parameter, without a deploy.

This package is the **official PHP SDK** (PHP `^8.1`) that brings the Traffical parameter control plane to your PHP services.

Features
--------

[](#features)

- **Local, in-process evaluation** — resolve a cached config bundle with no per-decision network call.
- **Typed parameters with safe defaults** — bool / string / number / JSON, each with a caller-provided fallback.
- **Layered experiments &amp; targeting** — Google-style layered isolation, condition/attribute segmentation, and progressive (percentage) rollouts.
- **Adaptive optimization** — contextual-bandit scoring evaluated client-side.
- **BYO warehouse-native assignment logging** — route structured assignment rows through your own pipeline (Segment, RudderStack, a DB, a queue) so assignment data never has to leave your infrastructure.
- **Event tracking** — exposure, decision, and custom track events, batched and flushed PHP-FPM-aware via `fastcgi_finish_request()` so the response returns before events are sent.
- **Plugin system** — hook into the `decide` / `exposure` / `track` lifecycle from day one.
- **PSR-first &amp; framework-ready** — PSR-18 HTTP, PSR-17 factories, PSR-16 cache, PSR-3 logger, and PSR-20 clock are all injectable; first-party Laravel, Symfony, and OpenFeature integrations.

Modes
-----

[](#modes)

- **Bundle mode (default)** — the SDK fetches a config bundle, caches it (shared across PHP-FPM workers via a PSR-16 store), and resolves every parameter locally. No per-decision network call.
- **Server mode** — resolution is delegated to the Traffical edge via `POST /v1/resolve` (cached per request). Use it when you want zero client-side evaluation logic.

Further reading
---------------

[](#further-reading)

- [docs/warehouse-native.md](docs/warehouse-native.md) — BYO assignment logging and warehouse-native metrics
- [docs/plugins.md](docs/plugins.md) — the plugin lifecycle and built-in plugins
- [docs/php-lifecycle.md](docs/php-lifecycle.md) — event batching and flushing under PHP-FPM
- [docs/conformance.md](docs/conformance.md) — cross-language determinism and the bundle-vs-server tradeoffs
- [examples/](examples/) — runnable examples: basic, server mode, warehouse-native BYO, custom plugin, plus [Laravel](examples/laravel.md) and [Symfony](examples/symfony.md) guides

Installation
------------

[](#installation)

```
composer require traffical/sdk
```

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](https://docs.php-http.org/en/latest/discovery.html):

```
composer require guzzlehttp/guzzle nyholm/psr7
# or: composer require symfony/http-client nyholm/psr7
```

Quickstart (bundle mode)
------------------------

[](#quickstart-bundle-mode)

```
use Traffical\Client;
use Traffical\ClientOptions;

$client = new Client(new ClientOptions(
    orgId: 'org_...',
    projectId: 'prj_...',
    env: 'production',
    apiKey: 'your_sdk_key',
));

// Resolve parameters with defaults as the fallback.
$params = $client->getParams(
    context: ['userId' => 'user-abc', 'country' => 'US'],
    defaults: ['checkout_button_color' => 'blue', 'discount_pct' => 0],
);

$color = $params['checkout_button_color'];
```

### Decide + track exposure

[](#decide--track-exposure)

`decide()` returns a `DecisionResult` (assignments + metadata). Call `trackExposure()` when the user actually sees the treatment so exposures are attributed correctly.

```
$decision = $client->decide(
    context: ['userId' => 'user-abc'],
    defaults: ['hero_variant' => 'control'],
);

$variant = $decision->assignments['hero_variant'];

// ...render the variant, then record that the user saw it:
$client->trackExposure($decision);

// Custom analytics events. Optional args (decisionId, value, values, unitKey,
// eventTimestamp) live in a TrackOptions bag:
$client->track('checkout_completed', ['orderId' => 'o-1001'], new Traffical\TrackOptions(
    decisionId: $decision->decisionId,
    value: 49.0,
));

// Flush is automatic on shutdown; call explicitly in worker/CLI contexts:
$client->flushEvents();

// In a long-lived process, close() runs teardown and awaits a final flush:
$client->close();
```

Server mode
-----------

[](#server-mode)

Delegate resolution to the edge worker instead of evaluating a local bundle. Each `getParams()`/`decide()`performs (and caches per request) a `POST /v1/resolve`.

```
$client = new Client(new ClientOptions(
    orgId: 'org_...',
    projectId: 'prj_...',
    env: 'production',
    apiKey: 'your_sdk_key',
    evaluationMode: 'server',
));
```

See [docs/conformance.md](docs/conformance.md) for the bundle-vs-server tradeoffs.

BYO warehouse-native assignment logging
---------------------------------------

[](#byo-warehouse-native-assignment-logging)

Pass an `assignmentLogger` to route structured rows through your own pipeline. The `WarehouseNativeLogger`helper maps each entry to a `snake_case` row (including the stable `policy_key`/`allocation_key` used for warehouse joins):

```
use Traffical\Client;
use Traffical\ClientOptions;
use Traffical\Warehouse\WarehouseNativeLogger;

$logger = new WarehouseNativeLogger(function (array $row): void {
    // INSERT $row into your warehouse / CDP / queue.
});

$client = new Client(new ClientOptions(
    orgId: 'org_...',
    projectId: 'prj_...',
    env: 'production',
    apiKey: 'your_sdk_key',
    assignmentLogger: $logger,
    disableCloudEvents: true, // keep assignment data on your own infra
));
```

Full guide: [docs/warehouse-native.md](docs/warehouse-native.md).

Plugins
-------

[](#plugins)

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

```
use Traffical\ClientOptions;
use Traffical\Plugins\DebugPlugin;

$options = new ClientOptions(/* ... */, plugins: [new DebugPlugin()]);
```

Full guide: [docs/plugins.md](docs/plugins.md).

Framework integrations
----------------------

[](#framework-integrations)

- **Laravel** — auto-discovered `TrafficalServiceProvider` + `Traffical` facade. See [examples/laravel.md](examples/laravel.md).
- **Symfony** — `TrafficalBundle` with a `traffical` config tree. See [examples/symfony.md](examples/symfony.md).
- **OpenFeature** — optional `TrafficalProvider` (requires `open-feature/sdk`).

PHP lifecycle
-------------

[](#php-lifecycle)

The client registers a shutdown handler that calls `fastcgi_finish_request()` (when available) so the HTTP response is returned to the user *before* events are flushed. See [docs/php-lifecycle.md](docs/php-lifecycle.md).

Configuration reference
-----------------------

[](#configuration-reference)

`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):

OptionDefaultDescription`orgId`, `projectId`, `env`, `apiKey`—Required scoping + auth`baseUrl``https://sdk.traffical.io`Control-plane base URL`localConfig``null`Bootstrap/offline `ConfigBundle``refreshIntervalMs``60000`Cached bundle TTL`evaluationMode``bundle``bundle` (local) or `server``assignmentLogger``null`BYO warehouse logger (callable or `AssignmentLogger`)`disableCloudEvents``false`Stop sending events to Traffical`deduplicateAssignmentLogger``true`Dedup logger calls per request`trackDecisions``true`Emit a decision event per `decide()``batchSize``10`Events per delivery batch (auto-flush threshold)`flushIntervalMs``30000`Event flush cadence (PHP flushes on batch-full or request end)`deduplicateExposures``true`Exposure session-dedup on/off (S4)`exposureSessionTtlMs``1800000`Exposure session-dedup TTL (30-minute session)`configTimeoutMs``10000`Config-fetch request timeout (see note below)`eventsTimeoutMs``10000`Event-delivery request timeout (see note below)`resolveTimeoutMs``5000`Server-resolve request timeout (see note below)`configSource`discoveredCustom `ConfigSource` (HTTP/file/inline/cached)`eventTransport`discoveredCustom `EventTransport` sink`httpClient`, `requestFactory`, `streamFactory`discoveredPSR-18/17 seams`cache``null`PSR-16 shared store (FPM workers share one bundle)`logger``NullLogger`PSR-3 logger`clock`systemPSR-20 clock`plugins``[]`Plugin list> **Request timeouts.** PSR-18 defines no portable timeout API, so the SDK cannot set connect/read timeouts on an arbitrary injected or auto-discovered HTTP client. The `configTimeoutMs` / `eventsTimeoutMs` / `resolveTimeoutMs`options carry the spec's intended values (10s / 10s / 5s); configure the matching timeout on the PSR-18 client you pass as `httpClient` (e.g. Guzzle's `connect_timeout` / `timeout`) to enforce them.

Cross-language conformance
--------------------------

[](#cross-language-conformance)

The PHP SDK shares the language-agnostic [Traffical SDK spec](tests/sdk-spec/test-vectors/README.md) with the JS/TS and Swift SDKs: the same SHA-256 v2 (UTF-8 byte) bucketing, the same layered resolution engine, and the same contextual-bandit scoring. Every release is gated on the spec's deterministic conformance vectors, so a given unit buckets identically on every platform. The fixtures are pinned via the `tests/sdk-spec` git submodule and run as part of `composer conformance`.

Development
-----------

[](#development)

```
composer install
composer test         # full PHPUnit suite (unit + conformance + integration)
composer conformance  # only the sdk-spec vectors
composer phpstan       # static analysis at level max
composer cs-check      # PSR-12 style check
```

Conformance fixtures are pinned via the `tests/sdk-spec` git submodule. After cloning:

```
git submodule update --init --recursive
```

License
-------

[](#license)

MIT

###  Health Score

36

—

LowBetter than 79% of packages

Maintenance98

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity34

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~40 days

Total

2

Last Release

10d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/37de8d177a47e3b4a68b716d016f939fcd3a35999298630959cf6c60057d1fe6?d=identicon)[traffical](/maintainers/traffical)

---

Top Contributors

[![marceltoben](https://avatars.githubusercontent.com/u/157526?v=4)](https://github.com/marceltoben "marceltoben (16 commits)")

---

Tags

feature-flagsab-testingbanditexperimentationtrafficalwarehouse-native

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/traffical-sdk/health.svg)

```
[![Health](https://phpackages.com/badges/traffical-sdk/health.svg)](https://phpackages.com/packages/traffical-sdk)
```

###  Alternatives

[flow-php/flow

PHP ETL - Extract Transform Load - Data processing framework

85036.3k](/packages/flow-php-flow)[tempest/framework

The PHP framework that gets out of your way.

2.2k34.4k16](/packages/tempest-framework)[symfony/symfony

The Symfony PHP framework

31.4k87.2M2.2k](/packages/symfony-symfony)[cakephp/cakephp

The CakePHP framework

8.8k19.5M1.8k](/packages/cakephp-cakephp)[telnyx/telnyx-php

Official Telnyx PHP SDK — APIs for Voice, SMS, MMS, WhatsApp, Fax, SIP Trunking, Wireless IoT, Call Control, and more. Build global communications on Telnyx's private carrier-grade network.

36789.4k2](/packages/telnyx-telnyx-php)[laudis/neo4j-php-client

Neo4j-PHP-Client is the most advanced PHP Client for Neo4j

185702.8k44](/packages/laudis-neo4j-php-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
