Flagception Bundle for Symfony

repository·master·Indexed 42 days ago

https://github.com/bestit/flagception-bundle

A feature toggle bundle for Symfony that manages feature flags via configuration, environment variables, and complex constraints. It includes a Symfony Profiler tab for debugging, support for custom FeatureActivators, Twig integration via the feature() function, and the ability to enable features for testing via browser cookies.

Tokens
12.8K
Snippets
51
Records
60
Agent score
51%

What's inside Flagception

  1. Understand the feature flag evaluation order

    master

    When multiple parameters are provided for a feature, Flagception follows a specific evaluation chain to determine if a feature is active:

    1. default: The default value is checked first.
    2. env: If the default value is false, the value from the environment variable specified in env is checked.
    3. constraint: If both default and env return false, the constraint logic is evaluated.

    If any step in this chain results in a true status, the feature is considered active.

    # config.yml
    
    flagception:
        features:      
            feature_123:
                default: false
                env: FEATURE_NAME_FROM_ENV  
                constraint: 'user_role == ROLE_ADMIN'
  2. How activators work in Flagception

    master

    Activators are responsible for resolving the state of a feature. When checking if a feature is active, the Feature Manager iterates through all registered activators and calls their isActive method.

    The Short-Circuit Rule: The manager stops at the first activator that returns true. If an activator returns true, no further activators in the chain will be queried. If an activator returns false, the manager proceeds to the next one.

    By default, the bundle includes a ConfigActivator which reads feature states from config.yml. You can implement custom activators to fetch feature states from external sources like remote servers or user roles.

    // The manager iterates through activators until one returns true
    if ($activator->isActive($featureName, $context)) {
        // Stop here, feature is active
    }
  3. Debug features with the Flagception Profiler

    master

    The bundle includes a Symfony Profiler tab that allows you to inspect feature toggle behavior during a request. The profiler displays:

    • How often a feature was requested.
    • The result returned (active or inactive).
    • The context provided during the evaluation.
  4. Use blacklist mode for cookie activation

    master

    By default, the cookie activator operates in whitelist mode, meaning only features defined in your config.yml can be enabled via cookies.

    If you set mode: 'blacklist' in your cookie configuration, the behavior changes: every feature can be enabled via the cookie unless it has been explicitly disabled in the feature configuration using cookie: false. This allows you to activate features that are not even defined in your current config.yml via the cookie.

    # config.yml
    
    flagception:
        features:      
            feature_123:
                default: false
                
            feature_456:
                default: false
                cookie: false
       
        activators:
            cookie:
                enable: true
                mode: 'blacklist'
  5. Apply constraints to features

    master

    Beyond simple true/false toggles, you can define a constraint in your configuration. A constraint is an expression that must evaluate to true or false for the feature to be active.

    Common usage patterns include:

    • User ID checks: user_id === 12
    • Role checks: "ROLE_ADMIN" in user_role
    • Time-based checks: (date("H") > 8 and date("H") < 18)
    • Complex logic: Combining checks with and / or operators.

    Constraints can be extended with custom variables and functions.

    flagception:
        features:      
            feature_123:
                default: false
                constraint: 'user_id === 12'     
                
            feature_abc:
                default: false
                constraint: '"ROLE_ADMIN" in user_role'   
                        
            feature_complex:
                default: false
                constraint: '(date("H") > 8 and date("H") < 18) or "ROLE_ADMIN" in user_role'
  6. Register a FeatureActivator service

    master

    If you are not using Symfony autoconfiguration, you must manually register your activator in your service configuration (e.g., services.yml) using the flagception.activator tag. You can optionally specify a priority to determine its position in the execution chain.

    flagception.activator.config_activator:
        class: Flagception\Bundle\FlagceptionBundle\Activator\ConfigActivator
        arguments:
            - '@flagception.constraint.constraint_resolver'
        tags:
            - { name: flagception.activator, priority: 100 }
  7. Create a custom FeatureActivator

    master

    To create a custom activator, implement the FeatureActivatorInterface. Your class must provide a unique name via getName() and logic to determine feature state via isActive($name, Context $context).

    Since Symfony 3.3, the bundle supports autoconfiguration for FeatureActivatorInterface. Simply implementing the interface and tagging the service with flagception.activator is sufficient. You can also provide a priority tag to control the order in which activators are checked.

    # AdminActivator.php
    
    class AdminActivator implements FeatureActivatorInterface
    {
        /**
         * {@inheritdoc}
         */
        public function getName()
        {
            # Return a unique name for this activator
            return 'admin';
        }
    
        /**
         * @var string $name The requested feature name (eg. 'feature_123')
         * @var Context $context The context object which contains key/values
         */
        public function isActive($name, Context $context)
        {
            # Example: Always return true if the user role contains 'ROLE_ADMIN'
            return in_array('ROLE_ADMIN', $context->get('user_roles'), true);
        }
    }
  8. Migrate from 2.x to 3.0

    master

    Upgrading from version 2.x to 3.0 involves a complete rework of the bundle. The underlying engine has changed to the Flagception library.

    Key breaking changes include:

    • Package Name: The composer package has changed from bestit/feature-toggle-bundle to flagception/flagception-bundle.
    • Namespaces: All namespaces and service IDs have been renamed to use the Flagception\ or Flagception\Bundle\FlagceptionBundle prefix.
    • Events: All previous events have been removed.
  9. Check feature flag state in Twig templates

    master

    You can verify if a feature flag is active within Twig templates using the feature() function or the is active feature test. This allows you to conditionally render UI elements based on the state of a flag.

    Simple check

    Use the feature() function with the feature name as the argument.

    Alternative syntax

    You can also use the is active feature test syntax for a more natural language feel.

    Check with context data

    To evaluate flags that have constraints (e.g., based on user roles or other attributes), pass a context array as the second argument to the feature() function. This context is used to satisfy the flag's requirements.

    {# Simple check #}
    {% if feature('feature_123') %}
        {# ... #}
    {% endif %}
    
    {# Alternative syntax #}
    {% if 'feature_123' is active feature %}
        {# ... #}
    {% endif %}
    
    {# Check with context data #}
    {% if feature('feature_123', {'role': 'ROLE_ADMIN'}) %}
        {# ... #}
    {% endif %}
  10. Protect routes with a single feature flag

    master

    To restrict access to a specific controller action based on a single feature flag, add the _feature key to the route's defaults. You can do this using PHP annotations, YAML configuration, or XML configuration.

    /**
     * @Route("/blog/{page}", defaults={"_feature": "feature_123"})
     */
    public function listAction($page)
    {
        // ...
    }
    # app/config/routing.yml
    blog_list:
        path:      /blog/{page}
        defaults:  { _controller: AppBundle:Blog:list, _feature: 'feature_789' }
    <!-- app/config/routing.xml -->
    <route id="blog_list" path="/blog/{page}">
        <default key="_controller">AppBundle:Blog:list</default>
        <default key="_feature">feature_123</default>
    </route>
  11. Protect routes with multiple feature flags

    master

    To require multiple feature flags to be active for a route to be accessible, pass an array of feature names to the _feature key in the route defaults. The route will only be accessible if all specified features are active.

    /**
     * @Route("/blog/{page}", defaults={"_feature": {"feature_123", "feature_456"}})
     */
    public function listAction($page)
    {
        // ...
    }
    # app/config/routing.yml
    blog_list:
        path:      /blog/{page}
        defaults:  { _controller: AppBundle:Blog:list, _feature: ['feature_456', 'feature_789'] }
    <!-- app/config/routing.xml -->
    <route id="blog_list" path="/blog/{page}">
        <default key="_controller">AppBundle:Blog:list</default>
        <default key="_feature">
            <list>
                <string>feature_123</string>
                <string>feature_456</string>
            </list>
        </default>
    </route>