PHPackages                             em411/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. em411/feature-flag-bundle

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

em411/feature-flag-bundle
=========================

Provides a feature flag mechanism (Symfony 4.4 / PHP 7.4 backport)

1.0.5(1mo ago)01.3k↓88.9%1MITPHPPHP &gt;=7.4CI passing

Since Aug 14Pushed 1mo agoCompare

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

READMEChangelog (6)Dependencies (13)Versions (15)Used By (1)

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

[](#featureflag-bundle)

[![CI](https://github.com/em411/feature-flag-bundle/actions/workflows/ci.yml/badge.svg)](https://github.com/em411/feature-flag-bundle/actions/workflows/ci.yml)[![Latest Stable Version](https://camo.githubusercontent.com/b6da3f920fe023d0929bc09893306c999c1491d1aab73a48834bda8b598d83c8/68747470733a2f2f706f7365722e707567782e6f72672f656d3431312f666561747572652d666c61672d62756e646c652f76)](https://packagist.org/packages/em411/feature-flag-bundle)[![License](https://camo.githubusercontent.com/bf16cf81aa3f55c0b81291cb5dbd95558feea90278fcddfb61179e52027af73a/68747470733a2f2f706f7365722e707567782e6f72672f656d3431312f666561747572652d666c61672d62756e646c652f6c6963656e7365)](https://packagist.org/packages/em411/feature-flag-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)**.

> **Symfony 4.4 / PHP 7.4 backport.** This is a fork of [ajgarlag/feature-flag-bundle](https://github.com/ajgarlag/feature-flag-bundle) refactored to run on Symfony 4.4 and PHP 7.4.

Which package should I use?
---------------------------

[](#which-package-should-i-use)

- **Running Symfony 6+ / PHP 8.1+?** Use the upstream [`ajgarlag/feature-flag-bundle`](https://github.com/ajgarlag/feature-flag-bundle) — it targets modern Symfony and uses native PHP 8 attributes.
- **Stuck on Symfony 4.4 / PHP 7.4?** Use this fork. It exists because the upstream cannot run on legacy stacks. The public API is intentionally kept compatible with the upstream so migration is a namespace-free drop-in.

> **Note on the namespace.** The Composer package is `em411/feature-flag-bundle` but the PHP namespace remains `Ajgarlag\FeatureFlagBundle\` on purpose, so code written against the upstream works here unchanged. Imports and the bundle class reference (`Ajgarlag\FeatureFlagBundle\FeatureFlagBundle::class`) stay the same.

🚀 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 em411/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 annotations
-------------------------------------

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

You can declare features using the `@AsFeature` annotation. This allows you to autoconfigure your features as services.

### On a class

[](#on-a-class)

You can use the annotation 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 annotation to specify a method to call on the service.

```
namespace App\Feature;

use Ajgarlag\FeatureFlagBundle\Attribute\AsFeature;

/**
 * @AsFeature(name="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 annotation on a method of a service. The method must be public.

```
namespace App\Feature;

use Ajgarlag\FeatureFlagBundle\Attribute\AsFeature;

final class FeatureService
{
    /**
     * @AsFeature(name="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;
    }
}
```

> **Note:** Unlike PHP 8 attributes, a Doctrine annotation cannot be repeated on the same class or method. To declare multiple features on one class, use the `ajgarlag.feature_flag.feature` service tag.

Gating routes by a feature
--------------------------

[](#gating-routes-by-a-feature)

Symfony 4.4 cannot evaluate services inside route `condition` expressions, so route-level feature gating is done in the controller (or a `kernel.controller`listener). Inject `FeatureCheckerInterface` and throw a 404 when the feature is off:

```
use Ajgarlag\FeatureFlagBundle\FeatureCheckerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class CheckoutController
{
    private $featureChecker;

    public function __construct(FeatureCheckerInterface $featureChecker)
    {
        $this->featureChecker = $featureChecker;
    }

    public function index(): Response
    {
        if (!$this->featureChecker->isEnabled('new_checkout')) {
            throw new NotFoundHttpException();
        }
        // ...
    }
}
```

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

[](#-providers)

Providers are responsible for returning the feature callables. 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): ?callable
    {
        // ...
    }
}
```

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

Doctrine example```
namespace App\FeatureProvider;

use Ajgarlag\FeatureFlagBundle\Provider\ProviderInterface;
use App\Repository\FeatureAssignmentRepository;

final class DoctrineProvider implements ProviderInterface
{
    private $featureAssignmentRepository;

    public function __construct(FeatureAssignmentRepository $featureAssignmentRepository)
    {
        $this->featureAssignmentRepository = $featureAssignmentRepository;
    }

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

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

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

```
# config/services.yaml
services:
    # A PSR-16 cache wrapping the "cache.unleash" pool, recommended to limit API calls.
    gitlab.unleash_cache:
        class: Symfony\Component\Cache\Psr16Cache
        arguments: ['@cache.unleash']

    gitlab.client_factory:
        class: Unleash\Client\UnleashBuilder
        factory: ['Unleash\Client\UnleashBuilder', 'createForGitlab']
        calls:
            - { method: withGitlabEnvironment, arguments: ['%env(GITLAB_ENVIRONMENT)%'], returns_clone: true }
            - { method: withAppUrl, arguments: ['%env(GITLAB_URL)%'], returns_clone: true }
            - { method: withInstanceId, arguments: ['%env(GITLAB_INSTANCE_ID)%'], returns_clone: true }
            - { method: withHttpClient, arguments: ['@psr18.http_client'], returns_clone: true }
            - { method: withCacheHandler, arguments: ['@gitlab.unleash_cache'], returns_clone: true }

    gitlab.client:
        class: Unleash\Client\Unleash
        factory: ['@gitlab.client_factory', 'build']

    App\FeatureProvider\GitlabProvider:
        arguments:
            $unleash: '@gitlab.client'
```

```
namespace App\FeatureProvider;

use Ajgarlag\FeatureFlagBundle\Provider\ProviderInterface;
use Symfony\Component\Security\Core\Security;
use Unleash\Client\Configuration\UnleashContext;
use Unleash\Client\Unleash;

class GitlabProvider implements ProviderInterface
{
    private $unleash;
    private $security;

    public function __construct(Unleash $unleash, Security $security)
    {
        $this->unleash = $unleash;
        $this->security = $security;
    }

    public function get(string $featureName): ?callable
    {
        $user = $this->security->getUser();

        // Set context. Example: user identifier, IP, hostname, etc.
        $context = new UnleashContext($user ? $user->getUsername() : null);

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

### Priority

[](#priority)

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

Set the priority on the service tag in your configuration:

```
# config/services.yaml
services:
    App\FeatureProvider\MyProvider:
        tags:
            - { name: 'ajgarlag.feature_flag.provider', priority: 10 }
```

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

42

—

FairBetter than 88% of packages

Maintenance88

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community11

Small or concentrated contributor base

Maturity44

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 61.1% 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 ~28 days

Recently: every ~0 days

Total

11

Last Release

59d ago

Major Versions

0.3.0 → 1.0.02026-05-22

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

0.1.1PHP &gt;=8.1

1.0.0PHP &gt;=7.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7682404?v=4)[Emil](/maintainers/em411)[@em411](https://github.com/em411)

---

Top Contributors

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

---

Tags

flagsfeatureflagtogglerolloutab testKill switchcanaryblue green

###  Code Quality

TestsPHPUnit

### Embed Badge

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

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

PHPackages © 2026

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