Skip to content

PHP SDK Reference

TL;DR: Run composer require featureflip/featureflip-php, call $client->boolVariation('flag-key', ['user_id' => 'u-123'], false), and you’re evaluating flags from PHP.

The featureflip/featureflip-php package is a server-side PHP SDK for evaluating feature flags locally — no per-request callouts to Featureflip. The SDK requires PHP 8.2 or later and is built around PSR standards: bring your own PSR-18 HTTP client, PSR-17 request factories, and PSR-16 simple cache, and the SDK plugs into them. This means it works cleanly with Laravel, Symfony, Slim, Mezzio, WordPress, Drupal, or any framework that already has Guzzle/HTTPlug and psr/cache in the autoloader.

PHP’s request-per-process model makes long-lived in-memory clients impractical, so the SDK is built around a cache-first strategy. On first use it fetches the full flag configuration once and writes it through to your PSR-16 cache; subsequent requests in the same minute (or however long your TTL is) read from cache without a network round trip. PSR-16 reserved characters ({}()/\@:) are escaped automatically in cache keys so the SDK plays nicely with Redis, Memcached, APCu, and filesystem cache backends. PascalCase enum values from the API (operators, evaluation reasons) are normalized via regex on the client side, so you compare against snake_case literals in your code.

The client is intentionally simple to mock in unit tests — interfaces, not final classes, are used throughout, so PHPUnit can mock them without final workarounds. Custom evaluation context is a plain associative array, the same shape used everywhere else in PHP. Targeting rules and percentage rollouts work identically to the other server SDKs, using the user_id key as the rollout bucket anchor.

View source on GitHub

Terminal window
composer require featureflip/featureflip-php

The package requires PSR-18 (HTTP client), PSR-17 (HTTP factories), and PSR-16 (simple cache) implementations. For example, with Guzzle and Symfony Cache:

Terminal window
composer require guzzlehttp/guzzle symfony/cache

The Config class accepts the following options:

OptionTypeDefaultDescription
baseUrlstring"https://eval.featureflip.io"Base URL of the Evaluation API
pollIntervalint30Polling interval in seconds
flushIntervalint30Interval in seconds between event flush batches
flushBatchSizeint100Maximum number of events per flush batch
initTimeoutint10Maximum time in seconds to wait for initialization
cacheCacheInterfacenullPSR-16 simple cache implementation (required)
httpClientClientInterfacenullPSR-18 HTTP client (required)
requestFactoryRequestFactoryInterfacenullPSR-17 request factory (required)
streamFactoryStreamFactoryInterfacenullPSR-17 stream factory (required)

Quick example with Guzzle and Symfony Cache

Section titled “Quick example with Guzzle and Symfony Cache”
use Featureflip\FeatureflipClient;
use Featureflip\Config;
use GuzzleHttp\Client as Guzzle;
use GuzzleHttp\Psr7\HttpFactory;
use Symfony\Component\Cache\Psr16Cache;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
$cache = new Psr16Cache(new FilesystemAdapter());
$guzzle = new Guzzle();
$factory = new HttpFactory();
$config = new Config(
baseUrl: 'https://eval.featureflip.io',
cache: $cache,
httpClient: $guzzle,
requestFactory: $factory,
streamFactory: $factory,
);
$client = FeatureflipClient::get('your-sdk-key', $config);

Create a client by calling FeatureflipClient::get() with an SDK key and configuration:

$client = FeatureflipClient::get('sdk-dev-abc123', $config);

The factory fetches the initial flag configuration synchronously if the cache is expired. A shutdown function is registered to flush pending events when the PHP process exits.

The SDK requires all four PSR dependencies to be provided:

  • CacheInterface (PSR-16) — for caching flag configurations between requests
  • ClientInterface (PSR-18) — for making HTTP requests to the Evaluation API
  • RequestFactoryInterface (PSR-17) — for creating HTTP request objects
  • StreamFactoryInterface (PSR-17) — for creating HTTP request body streams

An \InvalidArgumentException is thrown if any of these are missing.

FeatureflipClient::get() is a singleton-by-construction factory. Calling it multiple times with the same SDK key returns handles that share one underlying connection and flag store. This design makes the SDK safe to use with any DI container lifetime (singleton, scoped, or transient).

Each close() call decrements an internal reference count. The underlying resources (HTTP client, cache, event processor) are only cleaned up when the last handle is closed. Double-closing a handle is a safe no-op.

$h1 = FeatureflipClient::get('sdk-key', $config);
$h2 = FeatureflipClient::get('sdk-key'); // Shares the same core — no config needed
$h1->close(); // Core stays alive (h2 still holds a reference)
$h2->close(); // Last handle — core shuts down

All evaluation methods accept a flag key, an associative array context, and a typed default value. If the flag is not found or evaluation fails, the default value is returned. These methods never throw exceptions.

$enabled = $client->boolVariation('dark-mode', ['user_id' => 'u-123'], false);

Signature:

public function boolVariation(string $key, array $context, bool $default): bool
$plan = $client->stringVariation('pricing-tier', ['user_id' => 'u-123'], 'free');

Signature:

public function stringVariation(string $key, array $context, string $default): string
$limit = $client->numberVariation('rate-limit', ['user_id' => 'u-123'], 100);

Signature:

public function numberVariation(string $key, array $context, int|float $default): int|float

Returns a decoded associative array for JSON flags:

$config = $client->jsonVariation('banner-config', ['user_id' => 'u-123'], [
'text' => 'Welcome',
'color' => '#000',
]);

Signature:

public function jsonVariation(string $key, array $context, array $default): array

Returns an EvaluationDetail object with the evaluated value and metadata about the decision.

$detail = $client->variationDetail('dark-mode', ['user_id' => 'u-123'], false);
echo $detail->value; // true
echo $detail->reason; // "RULE_MATCH"
echo $detail->ruleId; // "rule-abc"
echo $detail->variationKey; // "true-variation"

Signature:

public function variationDetail(string $key, array $context, mixed $default): EvaluationDetail

A value object containing the evaluation result:

PropertyTypeDescription
valuemixedThe evaluated flag value
reasonstringWhy this value was returned
ruleIdstring|nullThe ID of the matched targeting rule, if applicable
variationKeystring|nullThe key of the matched variation
ValueDescription
FALLTHROUGHNo rules matched; the fallthrough variation was served
RULE_MATCHA targeting rule matched the context
FLAG_DISABLEDThe flag is disabled; the off variation was served
FLAG_NOT_FOUNDThe flag key does not exist
ERRORAn error occurred during evaluation

Records a custom analytics event.

$client->track('purchase-completed', ['user_id' => 'u-123'], [
'plan' => 'pro',
'amount' => 29.99,
]);

Signature:

public function track(string $eventKey, array $context, array $metadata = []): void

Sends user attributes for segment building and analytics.

$client->identify([
'user_id' => 'u-123',
'email' => '[email protected]',
'plan' => 'pro',
]);

Signature:

public function identify(array $context): void

Forces all buffered events to be sent to the server immediately.

$client->flush();

Flushes remaining events. If running under PHP-FPM, calls fastcgi_finish_request() to send the response before flushing.

$client->close();

A shutdown function is registered automatically when the client is created, so close() is called when the process ends. You can call it explicitly if you need to flush events before the process exits.

Creates a test client with fixed flag values. No network calls are made and no PSR implementations are required.

$client = FeatureflipClient::forTesting([
'dark-mode' => true,
'pricing-tier' => 'enterprise',
'rate-limit' => 500,
]);
$client->boolVariation('dark-mode', [], false); // true
$client->stringVariation('pricing-tier', [], 'free'); // "enterprise"

Signature:

public static function forTesting(array $flags): self

The client returns the override value for known flags, or the default value with a FLAG_NOT_FOUND reason for unknown flags.

The SDK is designed to work with any PSR-compatible libraries. Common combinations:

LibraryPSR-18PSR-17PSR-16
Guzzle + Symfony CacheGuzzleHttp\ClientGuzzleHttp\Psr7\HttpFactorySymfony\Component\Cache\Psr16Cache
Symfony HttpClient + CacheSymfony\Component\HttpClient\Psr18ClientNyholm\Psr7\Factory\Psr17FactorySymfony\Component\Cache\Psr16Cache
PHP-HTTP DiscoveryAuto-discoveredAuto-discoveredYour choice

The SDK does not include any HTTP client or cache implementation to keep it lightweight and avoid dependency conflicts.