Installation
composer require open-feature/sdk
Add to composer.json if using Laravel:
"require": {
"open-feature/sdk": "^2.1"
}
Basic Initialization
In your Laravel service provider (e.g., AppServiceProvider):
use OpenFeature\API;
use OpenFeature\NoOpProvider;
public function boot()
{
API::setProvider(new NoOpProvider()); // Fallback provider
}
First Feature Flag Evaluation
$flagValue = API::getBooleanValue('feature.my-flag', false);
// Use $flagValue in your logic
examples/ directory in the SDK repo for quick-start patterns.Feature Flag Evaluation
// Boolean flag
$isEnabled = API::getBooleanValue('feature.toggle', false);
// String flag (e.g., A/B test variants)
$variant = API::getStringValue('feature.variant', 'default');
// JSON flag (for complex configurations)
$config = API::getObjectValue('feature.config', []);
Provider Chaining (Multi-Provider Strategy)
use OpenFeature\ProviderRegistry;
$registry = new ProviderRegistry();
$registry->addProvider(new MyCustomProvider());
$registry->addProvider(new RemoteProvider());
API::setProvider($registry);
Context Management
use OpenFeature\Context;
$context = Context::build()
->withTargetingKey('user-123')
->withAttribute('country', 'US')
->build();
API::setContext($context);
Middleware for Context Injection
namespace App\Http\Middleware;
use Closure;
use OpenFeature\API;
use OpenFeature\Context;
class SetOpenFeatureContext
{
public function handle($request, Closure $next)
{
$context = Context::build()
->withTargetingKey($request->user()->id)
->withAttribute('user_role', $request->user()->role)
->build();
API::setContext($context);
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\SetOpenFeatureContext::class,
];
Service Container Binding
public function register()
{
$this->app->singleton(\OpenFeature\API::class, function ($app) {
return \OpenFeature\API::getInstance();
});
}
Dynamic Provider Switching
// In a config file (e.g., config/openfeature.php)
'providers' => [
'default' => \App\Providers\LocalProvider::class,
'staging' => \App\Providers\RemoteProvider::class,
],
Load dynamically in a service provider:
$providerClass = config('openfeature.providers.' . env('APP_ENV'));
API::setProvider(new $providerClass());
Event-Driven Flag Updates Use Laravel events to trigger flag refreshes:
// Listen for user role changes
event(new UserRoleUpdated($user));
In your provider:
public function evaluate($flagKey, $defaultValue)
{
if ($flagKey === 'feature.admin-dashboard') {
return auth()->user()->isAdmin();
}
return $defaultValue;
}
Context Leaks
API::clearContext() or wrap logic in a context block:
API::setContext($context);
try {
$result = API::getBooleanValue('flag', false);
} finally {
API::clearContext();
}
Provider Initialization Order
ProviderRegistry to define fallback behavior:
$registry = new ProviderRegistry();
$registry->addProvider(new RemoteProvider(), ProviderRegistry::PRIORITY_HIGH);
$registry->addProvider(new LocalProvider(), ProviderRegistry::PRIORITY_LOW);
API::setProvider($registry);
Thread Safety
API instance per thread or use a singleton with caution:
// In a job
$api = new \OpenFeature\API(new NoOpProvider());
$api->setContext($context);
Flag Key Collisions
feature.auth.2fa, feature.marketing.banner).Caching Headaches
$provider = new RemoteProvider();
$provider->setCacheTTL(300); // 5 minutes
Enable Logging
use OpenFeature\Log;
Log::setLogger(new \Monolog\Logger('openfeature', [
new \Monolog\Handler\StreamHandler(storage_path('logs/openfeature.log')),
]));
Inspect Provider Chain
$provider = API::getProvider();
if ($provider instanceof ProviderRegistry) {
foreach ($provider->getProviders() as $p) {
dump(get_class($p));
}
}
Test with NoOpProvider
API::setProvider(new NoOpProvider());
Ensures your logic works without external dependencies.
Custom Providers
Implement ProviderInterface:
class DatabaseProvider implements ProviderInterface
{
public function evaluate($flagKey, $defaultValue)
{
return DB::table('feature_flags')
->where('key', $flagKey)
->value('value') ?? $defaultValue;
}
// ... other methods
}
Hooks for Flag Evaluation
Use Hooks to intercept evaluations:
use OpenFeature\Hooks;
Hooks::addHook(Hooks::EVALUATE, function ($hook) {
if ($hook->getFlagKey() === 'feature.sensitive') {
$hook->setValue(false); // Override
}
});
Laravel Service Provider Integration Extend the SDK with Laravel-specific features:
public function boot()
{
API::setProvider(new class implements ProviderInterface {
public function evaluate($flagKey, $defaultValue)
{
return config("openfeature.flags.{$flagKey}", $defaultValue);
}
// ... other methods
});
}
Dynamic Flag Evaluation Combine with Laravel’s config or cache:
public function evaluate($flagKey, $defaultValue)
{
$cacheKey = "openfeature:{$flagKey}";
return Cache::remember($cacheKey, now()->addMinutes(5), function () use ($flagKey, $defaultValue) {
return DB::table('flags')->where('key', $flagKey)->value('value') ?? $defaultValue;
});
}
Batch Evaluations
$flags = [
'feature.a' => false,
'feature.b' => 'default',
];
$results = API::getValues($flags);
Lazy-Load Providers Load remote providers only when needed:
$provider = new LazyRemoteProvider(function () {
return new RemoteProvider(config('services.openfeature'));
});
Avoid Context Overhead Use minimal context attributes:
$context = Context::build()
->withTargetingKey($userId) // Required for most providers
->build();
How can I help you explore Laravel packages today?