PHP Feature Flags Quickstart
This guide gets you from zero to evaluating a feature flag in a PHP application. The SDK uses PSR-compatible interfaces and works with any framework.
Prerequisites
Section titled “Prerequisites”- A Featureflip account with at least one feature flag created
- An SDK key from your environment settings
- PHP 8.2+
- Composer
Install
Section titled “Install”composer require featureflip/featureflip-php guzzlehttp/guzzle symfony/cacheThis installs the SDK along with Guzzle (PSR-18 HTTP client and PSR-17 factories) and Symfony Cache (PSR-16 cache).
Initialize the client
Section titled “Initialize the client”<?php
require __DIR__ . '/vendor/autoload.php';
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());// Always give the HTTP client a timeout. The SDK fetches its configuration on// the calling thread, and Guzzle's default is no timeout at all — so without// one, an Evaluation API that stalls can block the request.$guzzle = new Guzzle(['timeout' => 5, 'connect_timeout' => 2]);$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);The factory fetches your flag configuration when it is due a refresh, and registers a shutdown function to flush events automatically. The same check runs when you evaluate a flag, so a client held across many requests — Laravel Octane, RoadRunner, FrankenPHP, Swoole — keeps picking up changes rather than freezing at whatever it booted with.
The SDK key can also come from the FEATUREFLIP_SDK_KEY environment variable: pass an empty string and it is read from there.
Evaluate a flag
Section titled “Evaluate a flag”$showBanner = $client->boolVariation('new-banner', ['user_id' => 'user-123'], false);
if ($showBanner) { echo "Showing the new banner";} else { echo "Using the default experience";}Each variation method is typed to its return value. The default argument is returned when the flag is not found or evaluation fails.
$color = $client->stringVariation('banner-color', ['user_id' => 'user-123'], 'blue');$limit = $client->numberVariation('rate-limit', ['user_id' => 'user-123'], 100);Clean up
Section titled “Clean up”The SDK registers a shutdown function that flushes pending analytics events when the PHP process exits. You can also flush or close manually:
$client->flush(); // Send buffered events immediately$client->close(); // Flush events and finish the request (PHP-FPM)For long-running processes, call $client->close() when your application shuts down.
How it works
Section titled “How it works”The PHP SDK is a server-side SDK that evaluates flags locally in your PHP process. It fetches the full flag configuration from the Featureflip evaluation API and stores it in the PSR-16 cache you provide. Requests made before pollInterval has elapsed reuse that configuration without any API call. Because evaluation happens locally, boolVariation() and the other variation calls return immediately.
Unlike the other server SDKs, PHP has no background thread to poll on: it re-fetches when the interval has elapsed and something asks for a flag. Under PHP-FPM that is naturally once per interval across your workers; under a persistent runtime (Octane, RoadRunner, FrankenPHP, Swoole) the same check runs on the evaluation path, and you can call $client->refresh() from a tick hook to move the fetch between requests instead of inside one.
The stored configuration is kept, not expired on a timer. Only a successful fetch replaces it, so an evaluation API outage leaves your flags serving their last known good values instead of the defaults you pass at each call site.
Troubleshooting
Section titled “Troubleshooting”Flags are stale or do not update after changes in the dashboard
Flag changes take effect once pollInterval has elapsed — 30 seconds by default. Lower it for faster propagation:
$config = new Config(pollInterval: 5, /* ... */);Note this is a refresh interval, not a cache lifetime. A configuration that has been fetched successfully is kept, so if the Evaluation API becomes unreachable your flags keep serving their last known good values rather than falling back to the defaults you pass at each call site.
Every flag returns the default I passed
The SDK does not throw when it cannot load a configuration — it reports the failure and serves your defaults, so a bad SDK key or an unreachable Evaluation API looks like “every flag is off” rather than an exception. Two things to check:
if (!$client->isInitialized()) { // Nothing was ever loaded — the SDK is serving your defaults.}and the log. The SDK writes to PHP’s error log by default, or to any PSR-3 logger you pass as logger:. A rejected key looks like:
[featureflip] flag configuration fetch failed: HTTP 401 from /v1/sdk/flags- no cached configuration is available, so flags will serve their caller defaultsVerify the key belongs to the environment you expect, and that baseUrl includes the protocol (https://).
InvalidArgumentException from the cache layer
PSR-16 cache keys have reserved characters ({}()/\@:). The SDK handles this internally, but if you see this error, ensure you are using a PSR-16 compliant cache implementation. Symfony Cache and Laravel’s cache are both compatible.
Next steps
Section titled “Next steps”- PHP SDK reference — full API documentation
- Targeting rules — deliver different values to different users
- Rollout strategies — gradually release features with percentage rollouts
- How to Create Feature Flags — create and manage flags from the dashboard
- Environments — manage separate configurations for dev, staging, and production