PHPackages                             ajgarlag/feature-flag-bundle - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. ajgarlag/feature-flag-bundle

ActiveSymfony-bundle[Utility &amp; Helpers](/categories/utility)

ajgarlag/feature-flag-bundle
============================

Provides a feature flag mechanism

0.2.0(2mo ago)1417.8k↓38%1MITPHPPHP &gt;=8.1CI passing

Since Aug 14Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/ajgarlag/feature-flag-bundle)[ Packagist](https://packagist.org/packages/ajgarlag/feature-flag-bundle)[ Docs](https://github.com/ajgarlag/feature-flag-bundle)[ RSS](/packages/ajgarlag-feature-flag-bundle/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (20)Versions (6)Used By (0)

FeatureFlag Bundle
==================

[](#featureflag-bundle)

The FeatureFlag Bundle allows you to split the code execution flow by enabling features depending on context.

It provides a service that checks if a feature is enabled. Each feature is defined by a callable function that returns a value. The feature is enabled if the value matches the expected one (mostly a boolean but not limited to).

**This bundle code has been borrowed from [symfony/symfony#53213](https://github.com/symfony/symfony/pull/53213)**.

Important

The purpose of this bundle is to allow you to test the code proposed in the PR.

🚀 Getting Started
-----------------

[](#-getting-started)

Follow these steps to install and use the bundle in your Symfony application.

### Step 1: Download the bundle

[](#step-1-download-the-bundle)

Open a command console, enter your project directory and execute the following command to download the latest stable version of this bundle:

```
composer require ajgarlag/feature-flag-bundle
```

### Step 2: Enable the bundle (for non-Flex applications)

[](#step-2-enable-the-bundle-for-non-flex-applications)

Then, enable the bundle by adding it to the list of registered bundles in the `config/bundles.php` file of your project:

```
// config/bundles.php

return [
    // ...
    Ajgarlag\FeatureFlagBundle\FeatureFlagBundle::class => ['all' => true],
];
```

✨ Declaring features with attributes
------------------------------------

[](#-declaring-features-with-attributes)

You can declare features using the `#[AsFeature]` attribute. This allows you to autoconfigure your features as services.

### On a class

[](#on-a-class)

You can use the attribute on an invokable class:

```
namespace App\Feature;

use Ajgarlag\FeatureFlagBundle\Attribute\AsFeature;

#[AsFeature('xmas')]
final class XmasFeature
{
    public function __invoke(): bool
    {
        return date('m-d') === '12-25';
    }
}
```

The feature will be named `xmas`. If you don't provide a name, the FQCN of the class will be used.

You can also use the `method` property of the attribute to specify a method to call on the service.

```
namespace App\Feature;

use Ajgarlag\FeatureFlagBundle\Attribute\AsFeature;

#[AsFeature('xmas', method: 'isXmas')]
final class XmasFeature
{
    public function isXmas(): bool
    {
        return date('m-d') === '12-25';
    }
}
```

### On a method

[](#on-a-method)

You can also use the attribute on a method of a service. The method must be public.

```
namespace App\Feature;

use Ajgarlag\FeatureFlagBundle\Attribute\AsFeature;

final class FeatureService
{
    #[AsFeature('weekend')]
    public function isWeekend(): bool
    {
        return date('N') >= 6;
    }

    #[AsFeature] // The feature will be named "App\Feature\FeatureService::isAnotherFeature"
    public function isAnotherFeature(): bool
    {
        return true;
    }
}
```

🗺️ Routing integration
----------------------

[](#️-routing-integration)

The bundle provides two functions to use in route conditions: `feature_is_enabled` and `feature_get_value`.

You can use them to enable or disable routes based on features.

```
namespace App\Controller;

use Symfony\Component\Routing\Attribute\Route;

class SomeController
{
    #[Route('/some/path', condition: "feature_is_enabled('some_feature')")]
    public function index()
    {
        // ...
    }
}
```

You can also use `feature_get_value` to check for a specific value.

```
namespace App\Controller;

use Symfony\Component\Routing\Attribute\Route;

class SomeController
{
    #[Route('/some/path', condition: "feature_get_value('some_feature') == 'some_value'")]
    public function index()
    {
        // ...
    }
}
```

🧩 Providers
-----------

[](#-providers)

Providers are responsible for returning the feature closures. You can create your own provider by implementing the `ProviderInterface`.

Any service that implements `ProviderInterface` is automatically registered as a provider. The bundle comes with a `ChainProvider` that allows you to combine multiple providers. The first provider that returns a feature wins.

### The ProviderInterface

[](#the-providerinterface)

To create your own provider, you need to implement the `ProviderInterface`.

```
namespace App\FeatureProvider;

use Ajgarlag\FeatureFlagBundle\Provider\ProviderInterface;

class MyProvider implements ProviderInterface
{
    public function get(string $featureName): ?\Closure
    {
        // ...
    }
}
```

The `get` method must return a `\Closure` if the provider has the feature, or `null` otherwise.

Doctrine example```
namespace App\FeatureProvider;

use App\Repository\FeatureAssignmentRepository;
use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;
use Symfony\Component\FeatureFlag\Provider\ProviderInterface;

final class DoctrineProvider implements ProviderInterface
{
    public function __construct(
        private readonly FeatureAssignmentRepository $featureAssignmentRepository,
    ) {
    }

    public function get(string $featureName): ?\Closure
    {
        // Set context. Example: user identifier, IP, hostname, etc.
        $context = [];

        return function () use ($featureName) {
            return $this->featureAssignmentRepository->featureIsEnabled($featureName, $context);
        };
    }
}
```

Gitlab exampleFirst, declare a service to interact with the [Unleash](https://www.getunleash.io/) API.

```
// config/services.php

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Symfony\Component\Cache\Psr16Cache;
use Unleash\Client\Unleash;
use Unleash\Client\UnleashBuilder;

return function(ContainerConfigurator $container): void {

    // Application service definition

    $services->set('gitlab.client_factory')
        ->class(UnleashBuilder::class)
        ->factory([UnleashBuilder::class, 'createForGitlab'])
        ->call('withGitlabEnvironment', [env('GITLAB_ENVIRONMENT')], true)
        ->call('withAppUrl', [env('GITLAB_URL')], true)
        ->call('withInstanceId', [env('GITLAB_INSTANCE_ID')], true)
        ->call('withHttpClient', [service('psr18.http_client')], true)
        // Using a cache is recommended to limit API calls (named "cache.unleash" in this example)
        ->call('withCacheHandler', [inline_service(Psr16Cache::class)->args([service('cache.unleash')])], true)
    ;

    $services->set('gitlab.client')
        ->class(Unleash::class)
        ->factory([service('gitlab.client_factory'), 'build'])
    ;
};
```

```
namespace App\FeatureProvider;

use Symfony\Bundle\SecurityBundle\Security;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\FeatureFlag\Provider\ProviderInterface;
use Unleash\Client\Configuration\Context;
use Unleash\Client\Configuration\UnleashContext;
use Unleash\Client\Unleash;

class GitlabProvider implements ProviderInterface
{
    public function __construct(
        #[Autowire(service: 'gitlab.client')] private readonly Unleash $unleash,
        private readonly Security $security,
    ) {
    }

    public function get(string $featureName): ?\Closure
    {
        // Set context. Example: user identifier, IP, hostname, etc.
        $context = new UnleashContext(
            currentUserId: $this->security->getUser()?->getUserIdentifier()
        );

        return fn () => $this->unleash->isEnabled($featureName, $context);
    }
}
```

### Priority

[](#priority)

You can control the order of the providers in the chain using the `priority` attribute on the `ajgarlag.feature_flag.provider` tag.

You can use the `#[AutoconfigureTag]` attribute to set the priority of your provider.

```
namespace App\FeatureProvider;

use Ajgarlag\FeatureFlagBundle\Provider\ProviderInterface;
use Symfony\Component\DependencyInjection\Attribute\AutoconfigureTag;

#[AutoconfigureTag('ajgarlag.feature_flag.provider', ['priority' => 10])]
class MyProvider implements ProviderInterface
{
    // ...
}
```

Providers with a higher priority will be checked first.

🎨 Twig extension
----------------

[](#-twig-extension)

The bundle provides two functions to use in Twig templates: `feature_is_enabled` and `feature_get_value`.

```
{% if feature_is_enabled('some_feature') %}
    {# ... #}
{% endif %}

{% if feature_get_value('some_feature') == 'some_value' %}
    {# ... #}
{% endif %}
```

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance86

Actively maintained with recent releases

Popularity36

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

 Bus Factor1

Top contributor holds 94.2% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~68 days

Total

4

Last Release

74d ago

PHP version history (2 changes)0.1.0PHP &gt;=8.2

0.1.1PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/f895b5880bdb002b96abac69a1a6630b4e20c4fbff9dbe3d47b08f0d1b7e2211?d=identicon)[ajgarlag](/maintainers/ajgarlag)

---

Top Contributors

[![ajgarlag](https://avatars.githubusercontent.com/u/388184?v=4)](https://github.com/ajgarlag "ajgarlag (49 commits)")[![Jean-Beru](https://avatars.githubusercontent.com/u/6114779?v=4)](https://github.com/Jean-Beru "Jean-Beru (3 commits)")

---

Tags

symfonysymfony-bundleflagsfeatureflagtogglerolloutab testKill switchcanaryblue green

###  Code Quality

TestsPHPUnit

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/ajgarlag-feature-flag-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/ajgarlag-feature-flag-bundle/health.svg)](https://phpackages.com/packages/ajgarlag-feature-flag-bundle)
```

###  Alternatives

[opensoft/rollout

Feature switches or flags for PHP

2571.8M5](/packages/opensoft-rollout)[flagception/flagception-bundle

Feature toggle bundle on steroids.

283.8M](/packages/flagception-flagception-bundle)[zumba/swivel

Strategy driven feature toggles

209135.7k5](/packages/zumba-swivel)[qandidate/toggle

Feature toggling for your PHP application.

3812.0M9](/packages/qandidate-toggle)[francescomalatesta/laravel-feature

A simple package to manage feature flagging in a Laravel project.

211206.4k](/packages/francescomalatesta-laravel-feature)[flagception/flagception

Feature toggle on steroids.

134.3M5](/packages/flagception-flagception)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
