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.
Installation
Section titled “Installation”composer require featureflip/featureflip-phpThe package requires PSR-18 (HTTP client), PSR-17 (HTTP factories), and PSR-16 (simple cache) implementations. For example, with Guzzle and Symfony Cache:
composer require guzzlehttp/guzzle symfony/cacheConfiguration
Section titled “Configuration”The Config class accepts the following options:
| Option | Type | Default | Description |
|---|---|---|---|
baseUrl | string | "https://eval.featureflip.io" | Base URL of the Evaluation API |
pollInterval | int | 30 | Polling interval in seconds |
flushInterval | int | 30 | Interval in seconds between event flush batches |
flushBatchSize | int | 100 | Maximum number of events per flush batch |
initTimeout | int | 10 | Maximum time in seconds to wait for initialization |
cache | CacheInterface | null | PSR-16 simple cache implementation (required) |
httpClient | ClientInterface | null | PSR-18 HTTP client (required) |
requestFactory | RequestFactoryInterface | null | PSR-17 request factory (required) |
streamFactory | StreamFactoryInterface | null | PSR-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);Initialization
Section titled “Initialization”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.
Required PSR implementations
Section titled “Required PSR implementations”The SDK requires all four PSR dependencies to be provided:
CacheInterface(PSR-16) — for caching flag configurations between requestsClientInterface(PSR-18) — for making HTTP requests to the Evaluation APIRequestFactoryInterface(PSR-17) — for creating HTTP request objectsStreamFactoryInterface(PSR-17) — for creating HTTP request body streams
An \InvalidArgumentException is thrown if any of these are missing.
Lifetime
Section titled “Lifetime”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 downEvaluating Flags
Section titled “Evaluating Flags”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.
boolVariation()
Section titled “boolVariation()”$enabled = $client->boolVariation('dark-mode', ['user_id' => 'u-123'], false);Signature:
public function boolVariation(string $key, array $context, bool $default): boolstringVariation()
Section titled “stringVariation()”$plan = $client->stringVariation('pricing-tier', ['user_id' => 'u-123'], 'free');Signature:
public function stringVariation(string $key, array $context, string $default): stringnumberVariation()
Section titled “numberVariation()”$limit = $client->numberVariation('rate-limit', ['user_id' => 'u-123'], 100);Signature:
public function numberVariation(string $key, array $context, int|float $default): int|floatjsonVariation()
Section titled “jsonVariation()”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): arrayEvaluation Details
Section titled “Evaluation Details”variationDetail()
Section titled “variationDetail()”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; // trueecho $detail->reason; // "RULE_MATCH"echo $detail->ruleId; // "rule-abc"echo $detail->variationKey; // "true-variation"Signature:
public function variationDetail(string $key, array $context, mixed $default): EvaluationDetailEvaluationDetail
Section titled “EvaluationDetail”A value object containing the evaluation result:
| Property | Type | Description |
|---|---|---|
value | mixed | The evaluated flag value |
reason | string | Why this value was returned |
ruleId | string|null | The ID of the matched targeting rule, if applicable |
variationKey | string|null | The key of the matched variation |
Evaluation reasons
Section titled “Evaluation reasons”| Value | Description |
|---|---|
FALLTHROUGH | No rules matched; the fallthrough variation was served |
RULE_MATCH | A targeting rule matched the context |
FLAG_DISABLED | The flag is disabled; the off variation was served |
FLAG_NOT_FOUND | The flag key does not exist |
ERROR | An error occurred during evaluation |
Event Tracking
Section titled “Event Tracking”track()
Section titled “track()”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 = []): voididentify()
Section titled “identify()”Sends user attributes for segment building and analytics.
$client->identify([ 'user_id' => 'u-123', 'plan' => 'pro',]);Signature:
public function identify(array $context): voidLifecycle
Section titled “Lifecycle”flush()
Section titled “flush()”Forces all buffered events to be sent to the server immediately.
$client->flush();close()
Section titled “close()”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.
Testing
Section titled “Testing”FeatureflipClient::forTesting()
Section titled “FeatureflipClient::forTesting()”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): selfThe client returns the override value for known flags, or the default value with a FLAG_NOT_FOUND reason for unknown flags.
PSR Compatibility
Section titled “PSR Compatibility”The SDK is designed to work with any PSR-compatible libraries. Common combinations:
| Library | PSR-18 | PSR-17 | PSR-16 |
|---|---|---|---|
| Guzzle + Symfony Cache | GuzzleHttp\Client | GuzzleHttp\Psr7\HttpFactory | Symfony\Component\Cache\Psr16Cache |
| Symfony HttpClient + Cache | Symfony\Component\HttpClient\Psr18Client | Nyholm\Psr7\Factory\Psr17Factory | Symfony\Component\Cache\Psr16Cache |
| PHP-HTTP Discovery | Auto-discovered | Auto-discovered | Your choice |
The SDK does not include any HTTP client or cache implementation to keep it lightweight and avoid dependency conflicts.
See also
Section titled “See also”- PHP Quickstart — get started in under 5 minutes
- Targeting & Segments — control which users see which variation
- Rollout Strategies — gradual percentage-based rollouts
- Evaluation API — REST API reference
- SDK Overview — compare server-side and client-side SDKs