Installation:
composer require check24/feature-flag-bundle
Enable the bundle in config/bundles.php:
return [
// ...
Shopping\FeatureFlagBundle\FeatureFlagBundle::class => ['all' => true],
];
Define Flags in .env:
FEATURE_FLAGS=foobar=true,another_feature=false
Flags are space-separated KEY=VALUE pairs (case-sensitive).
First Use Case: Check a flag in a controller:
use Shopping\FeatureFlagBundle\Service\FeatureFlagInterface;
class TestController {
public function __construct(private FeatureFlagInterface $featureFlag) {}
public function index() {
if ($this->featureFlag->isActive('foobar')) {
return new Response('Feature enabled!');
}
return new Response('Default behavior.');
}
}
config/packages/shopping_feature_flag.yaml (default config)src/Shopping/FeatureFlagBundle/Resources/config/services.yaml (service definitions)Use @IsActive annotation to gate routes:
/**
* @Route("/experimental")
* @IsActive("experimental_feature")
*/
public function experimentalRoute() { ... }
doctrine/annotations is installed).Inject FeatureFlagInterface into services for logic branching:
class AnalyticsService {
public function __construct(private FeatureFlagInterface $flags) {}
public function trackEvent() {
if ($this->flags->isActive('analytics_v2')) {
$this->trackWithV2();
} else {
$this->trackWithV1();
}
}
}
Render UI conditionally:
{% if is_active('new_ui') %}
<button class="btn btn-primary">New Design</button>
{% else %}
<button class="btn btn-default">Legacy</button>
{% endif %}
Extend functionality (e.g., database-backed flags):
# config/packages/shopping_feature_flag.yaml
shopping_feature_flag:
providers:
- { id: 'env', class: 'Shopping\FeatureFlagBundle\Provider\EnvProvider' }
- { id: 'db', class: 'App\Provider\DatabaseFlagProvider' } # Custom
class DatabaseFlagProvider implements ProviderInterface {
public function isActive(string $flag): bool {
return DB::table('feature_flags')->where('name', $flag)->value('enabled') ?? false;
}
}
.env for dev/staging/prod parity.shopping_feature_flag:
providers:
- { id: 'cookie', class: 'Shopping\FeatureFlagBundle\Provider\CookieProvider' }
Flags persist per user session (useful for A/B tests).providers:
- { id: 'user_agent', class: 'Shopping\FeatureFlagBundle\Provider\UserAgentProvider' }
Annotation Parsing:
@IsActive requires doctrine/annotations and Symfony’s annotation reader.doctrine/annotations is installed and annotations are loaded in services.yaml:
parameters:
annotation_reader.class: doctrine/annotations-php80:AnnotationReader
Case Sensitivity:
.env are case-sensitive (FEATURE=flag ≠ FEATURE=Flag).FEATURE_NEW_CHECKOUT=true).Provider Order:
bin/console debug:container Shopping\FeatureFlagBundle\Service\FeatureFlag to inspect provider chain.Caching:
cache_enabled)..env changes without cache invalidation..env updates:
php bin/console cache:clear
Cookie Provider Quirks:
same-site cookies for cross-subdomain consistency.Log Provider Results:
Override FeatureFlagInterface to log evaluations:
class DebugFeatureFlag implements FeatureFlagInterface {
public function isActive(string $flag): bool {
$result = $this->delegate->isActive($flag);
\Log::debug("Flag {$flag} evaluated to {$result}");
return $result;
}
}
Check Provider Chain: Dump the active providers:
$providers = $container->get('shopping_feature_flag.provider.chain');
dump($providers->getProviders());
Custom Providers:
Implement ProviderInterface:
class DatabaseProvider implements ProviderInterface {
public function isActive(string $flag): bool {
// Custom logic (e.g., Redis, API call)
}
}
Register in config/packages/shopping_feature_flag.yaml.
Override Default Behavior:
Extend FeatureFlag service:
services:
Shopping\FeatureFlagBundle\Service\FeatureFlag:
class: App\Service\CustomFeatureFlag
arguments:
$providers: !tagged 'shopping_feature_flag.provider'
Event Listeners: Listen for flag changes (if using dynamic providers):
class FlagChangeListener {
public function onKernelRequest(GetResponseEvent $event) {
if ($event->isMasterRequest()) {
$this->featureFlag->reload(); // Force refresh
}
}
}
shopping_feature_flag:
cache_enabled: false
isAnyActive() or isAllActive() methods if available (check bundle version).false (configurable via default_value in config).FEATURE_FLAGS_ for namespace separation:
FEATURE_FLAGS_NEW=checkout=true,ui_v2=false
Configure in shopping_feature_flag.env_prefix.How can I help you explore Laravel packages today?