signakit / flags-php
Official PHP SDK for SignaKit Feature Flags
Requires
- php: >=8.1
Requires (Dev)
- guzzlehttp/guzzle: ^7.0
- phpunit/phpunit: ^11.0
Suggests
- guzzlehttp/guzzle: Recommended HTTP client. Falls back to cURL if not installed.
README
Official PHP 8.1+ SDK for SignaKit Feature Flags.
- Framework-agnostic — works with Laravel, Symfony, WordPress, or plain PHP
- Evaluates flags locally (no network call per evaluation)
- MurmurHash3 bucketing for deterministic, consistent assignments
- ETag-based config caching to minimize CDN traffic
- Fire-and-forget event tracking
Installation
composer require signakit/flags-php
Guzzle is recommended but optional — the SDK falls back to cURL automatically:
composer require guzzlehttp/guzzle
Quick Start
Plain PHP
<?php require 'vendor/autoload.php'; use SignaKit\FlagsPhp\SignaKitClient; $client = new SignaKitClient('sk_prod_abc123_1234_xxxxxxxxxxxx'); $client->initialize(); // fetches config from CDN, throws on failure $ctx = $client->createUserContext('user-123', ['plan' => 'premium', 'country' => 'US']); $decision = $ctx->decide('new-checkout'); if ($decision?->enabled && $decision->variationKey === 'treatment') { // show new checkout } else { // show control } // Track a conversion $ctx->trackEvent('purchase', value: 99.99);
Laravel
// AppServiceProvider::register() $this->app->singleton(SignaKitClient::class, function () { $client = new SignaKitClient(config('services.signakit.sdk_key')); $client->initialize(); return $client; }); // In a controller / middleware public function show(Request $request, SignaKitClient $flags): Response { $ctx = $flags->createUserContext($request->user()->id, [ 'plan' => $request->user()->plan, 'country' => $request->header('CF-IPCountry', 'US'), ]); $decision = $ctx->decide('new-checkout'); return view('checkout', ['variant' => $decision?->variationKey ?? 'control']); }
WordPress
// functions.php or a plugin bootstrap file add_action('init', function () { $client = new \SignaKit\FlagsPhp\SignaKitClient(defined('SIGNAKIT_SDK_KEY') ? SIGNAKIT_SDK_KEY : ''); $client->initialize(); $GLOBALS['signakit'] = $client; }); // In a template $ctx = $GLOBALS['signakit']->createUserContext(get_current_user_id(), ['plan' => 'free']); $decision = $ctx->decide('new-header'); if ($decision?->variationKey === 'treatment') { get_template_part('partials/header-new'); } else { get_template_part('partials/header'); }
API Reference
SignaKitClient
new SignaKitClient(string $sdkKey, ?HttpClientInterface $httpClient = null)
| Method | Description |
|---|---|
initialize(): void |
Fetch config from CDN. Retries 3× with exponential back-off. Throws on failure. |
onReady(): void |
Alias for initialize(). |
createUserContext(string $userId, array $attributes = []): SignaKitUserContext |
Create an evaluation context for a user. |
refreshConfig(): void |
Re-fetch config (ETag-aware). Call periodically to pick up flag changes. |
SignaKitUserContext
$ctx = $client->createUserContext('user-123', ['plan' => 'premium']);
| Method | Description |
|---|---|
decide(string $flagKey): ?Decision |
Evaluate a single flag. Returns null if the flag does not exist or is archived. |
decideAll(): array<string, Decision> |
Evaluate every active flag. Returns a map of flagKey → Decision. |
trackEvent(string $eventKey, ?float $value = null): void |
Fire a conversion event. |
Decision
readonly class Decision { public string $flagKey; public string $variationKey; // 'off' when flag is stopped public bool $enabled; // false only when flag is stopped public ?string $ruleKey; // null when default allocation was used }
Custom HTTP Client
Implement HttpClientInterface to plug in your own transport:
use SignaKit\FlagsPhp\Contracts\HttpClientInterface; final class MyHttpClient implements HttpClientInterface { public function get(string $url, array $headers = []): array { /* ... */ } public function post(string $url, array $headers = [], string $body = ''): void { /* ... */ } } $client = new SignaKitClient('sk_prod_...', httpClient: new MyHttpClient());
How It Works
-
Config delivery — On
initialize()the SDK fetches a JSON config from CloudFront (d30l2rkped5b4m.cloudfront.net). The config contains all flag definitions, rules, and allocation ranges. Subsequent calls send anIf-None-MatchETag header so unchanged configs are never re-downloaded. -
Local evaluation — All
decide()calls run entirely in memory, with no network round-trip. The two-stage MurmurHash3 bucketing algorithm guarantees deterministic, consistent assignments: the same user always gets the same variation for a given flag configuration. -
Event tracking —
trackEvent()and internal$exposureevents are POSTed to an API Gateway endpoint asynchronously. Failures are logged witherror_log()and never throw.
SDK Key Format
sk_{env}_{orgId}_{projectId}_{random}
env—prod(production) ordev(development)orgId— alphanumeric organisation IDprojectId— numeric project IDrandom— 12-character hex suffix
Requirements
- PHP 8.1+
ext-curl(for the built-in cURL client) orguzzlehttp/guzzle ^7.0
License
MIT