Install the Bundle:
composer require aubes/openfeature-bundle
Register the bundle in config/bundles.php:
Aubes\OpenFeatureBundle\OpenFeatureBundle::class => ['all' => true],
Configure Flags (e.g., config/packages/open_feature.yaml):
open_feature:
flags:
new_checkout: true
dark_mode: false
max_items: 10
Use in a Controller (attribute-based):
use Aubes\OpenFeatureBundle\Attribute\FeatureFlag;
use Aubes\OpenFeatureBundle\Attribute\FeatureGate;
class CheckoutController {
#[FeatureGate('new_checkout')] // Blocks if flag is off
public function checkout(
#[FeatureFlag('dark_mode')] bool $darkMode,
#[FeatureFlag('max_items')] int $maxItems,
): Response {
return new Response("Dark mode: $darkMode, Max items: $maxItems");
}
}
Use in Twig:
{% if feature('new_checkout') %}
{# New checkout UI #}
{% endif %}
Kill Switch for a Feature:
open_feature.yaml:
open_feature:
flags:
experimental_api: false
#[FeatureGate('experimental_api')]
public function experimentalEndpoint(): Response { ... }
Attribute-Based Injection:
#[FeatureFlag] for typed flag values (e.g., bool, int, string).#[FeatureGate] to block execution if a flag is off.#[FeatureGate('feature_x')]
public function sensitiveAction(
#[FeatureFlag('timeout_ms')] int $timeout,
): void { ... }
Service Integration:
Client for programmatic access:
use OpenFeature\interfaces\flags\Client;
class MyService {
public function __construct(private readonly Client $client) {}
public function checkFlag(): bool {
return $this->client->getBooleanValue('feature_x', false);
}
}
Evaluation Context:
EvaluationContextProviderInterface to feed dynamic attributes (e.g., user ID, region):
use Aubes\OpenFeatureBundle\Context\EvaluationContextProviderInterface;
class UserContextProvider implements EvaluationContextProviderInterface {
public function getContext(): array {
return ['user_id' => $this->userService->getId()];
}
}
Hooks for Observability:
Hook for logging/tracing:
use OpenFeature\interfaces\hooks\Hook;
class LoggingHook implements Hook {
public function onFlagEvaluated(string $flagKey, mixed $value): void {
\Log::info("Flag $flagKey resolved to: " . json_encode($value));
}
}
openfeature.hook tag (Symfony auto-detects services).Provider Migration:
InMemoryProvider (dev) or EnvVarProvider (bootstrap):
open_feature:
provider: env_var # Uses env vars like `FEATURE_NEW_CHECKOUT=true`
Flagd or ConfigCat later by updating the provider config:
open_feature:
provider: flagd
flagd:
url: http://flagd:8080
EvaluationContextListener ensures context resets per request.open_feature:
provider: redis
redis:
dsn: redis://redis:6379
feature_value() for defaults:
{{ feature_value('max_items', 10) }} {# Falls back to 10 if flag is missing #}
Client in tests:
$this->container->set('open_feature.client', $this->createMock(Client::class));
Built-in Providers Are Limited:
InMemoryProvider/EnvVarProvider ignore EvaluationContext (no user targeting, rollouts).Double Hook Registration:
Hook service has both autoconfigure: true and tags: [openfeature.hook], it fires twice.tags in Symfony 6.4+ (autoconfiguration handles it).Context Leakage in Long-Running Runtimes:
EvaluationContextListener resets contexts per request (verified in 0.1.1+).Attribute Parsing Overhead:
#[FeatureFlag] adds reflection overhead. Avoid overusing on performance-critical paths.Client injection for hot paths.Redis Provider Key Format:
openfeature:{flag_key} in Redis.OpenFeature\SDK logging in config/packages/dev/monolog.yaml:
monolog:
handlers:
main:
processors:
- OpenFeature\SDK\Logging\LogFlagEvaluationsProcessor
OpenFeature\interfaces\flags\Provider and register via config:
open_feature:
provider: custom_provider
custom_provider:
class: App\OpenFeature\CustomProvider
EvaluationContextProviderInterface for runtime context (e.g., request-based):
class RequestContextProvider implements EvaluationContextProviderInterface {
public function __construct(private RequestStack $requestStack) {}
public function getContext(): array {
$request = $this->requestStack->getCurrentRequest();
return ['ip' => $request->getClientIp()];
}
}
RegexpValidatorHook to validate flag values:
services:
App\OpenFeature\Hooks\RegexpValidatorHook:
tags: [openfeature.hook]
arguments:
$pattern: '^[a-z0-9_-]+$'
open_feature:
provider: flagd
fallback_provider: env_var # Falls back to env vars if Flagd fails
How can I help you explore Laravel packages today?