定制 gracious/feature-flag-bundle 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

gracious/feature-flag-bundle

Composer 安装命令:

composer require gracious/feature-flag-bundle

包简介

Lightweight feature flag management for Symfony applications.

README 文档

README

Lightweight feature flags for Symfony. Define flags in config, then check them from services, controllers, Twig, routes, and controller attributes.

  • PHP >=8.3, Symfony ^7.0 || ^8.0, Twig 3 (optional).

Features

  • Config-driven flags with optional descriptions.
  • One default manager plus any number of named managers (separate flag groups).
  • Check flags in PHP, Twig, route defaults, or #[RequireFeature] attributes.
  • Per-process runtime overrides (enable / disable / reset).
  • Optional read-only REST endpoint with a kill switch.

Installation

composer require gracious/feature-flag-bundle

With Symfony Flex this is all you need. Without Flex, register the bundle manually:

// config/bundles.php
return [
    // ...
    Gracious\FeatureFlagBundle\GraciousFeatureFlagBundle::class => ['all' => true],
];

Quick start

# config/packages/gracious_feature_flag.yaml
gracious_feature_flag:
    flags:
        new_checkout:
            enabled: true
            description: 'New checkout flow'
        beta_search:
            enabled: false   # 'enabled' defaults to false
use Gracious\FeatureFlagBundle\Flag\FeatureFlagManagerInterface;

final class CheckoutService
{
    public function __construct(private FeatureFlagManagerInterface $flags) {}

    public function run(): void
    {
        if ($this->flags->isEnabled('new_checkout')) {
            // new flow
        }
    }
}

Configuration

gracious_feature_flag:
    # default manager flags
    flags:
        new_checkout: { enabled: true, description: 'New checkout flow' }
        beta_search:  { enabled: false }

    # optional: extra named managers, each its own flag group
    managers:
        billing:
            flags:
                invoices_v2: { enabled: true }

    # optional: exception thrown when a guard fails (route / attribute)
    exception:
        class: Gracious\FeatureFlagBundle\Exception\FeatureNotAvailableException
        status_code: 404
        factory: ~       # service id implementing ExceptionFactoryInterface; wins over 'class'

    # optional: REST endpoint kill switch
    api:
        enabled: true    # false => endpoints return 404

Manager API

$flags->isEnabled('new_checkout'); // bool
$flags->has('new_checkout');       // bool: flag is defined
$flags->get('new_checkout');       // Flag VO (name, enabled, description)
$flags->all();                     // array<string, Flag>

// runtime overrides (this PHP process only; see Limitations)
$flags->enable('beta_search');
$flags->disable('new_checkout');
$flags->reset('beta_search');      // back to the configured value

Unknown flag names throw UnknownFeatureException.

Named managers

Each named manager is its own service. Autowire it by the variable name <name>Manager:

public function __construct(FeatureFlagManagerInterface $billingManager) {}

To resolve a manager by name at runtime, inject the ManagerRegistry:

use Gracious\FeatureFlagBundle\Flag\ManagerRegistry;

public function __construct(private ManagerRegistry $registry) {}

$this->registry->get('billing')->isEnabled('invoices_v2');
$this->registry->getDefault()->isEnabled('new_checkout');

Unknown manager names throw UnknownManagerException.

Twig

{% if feature('new_checkout') %}
    <a href="/checkout/new">Try the new checkout</a>
{% endif %}

{# named manager (second argument) #}
{% if feature('invoices_v2', 'billing') %} ... {% endif %}

{# as a test #}
{% if 'beta_search' is feature_enabled %} ... {% endif %}

The Twig extension registers only when Twig is installed.

Route guards

Guard a route with the _feature_flag default. The string form requires the flag enabled:

beta_page:
    path: /beta
    controller: App\Controller\BetaController
    defaults:
        _feature_flag: beta_search

The array form sets the required state and an optional manager:

legacy_page:
    path: /legacy
    controller: App\Controller\LegacyController
    defaults:
        _feature_flag: { name: legacy, enabled: false, manager: default }

When the requirement is not met, the configured exception is thrown (404 by default).

Attribute guards

#[RequireFeature] works on a controller class or method and is repeatable:

use Gracious\FeatureFlagBundle\Attribute\RequireFeature;

#[RequireFeature('new_checkout')]                 // class: require enabled
final class CheckoutController
{
    #[RequireFeature('legacy', enabled: false)]   // require disabled
    public function index(): Response { /* ... */ }

    #[RequireFeature('invoices_v2', manager: 'billing')]
    public function invoices(): Response { /* ... */ }
}

Custom exception

For full control over the failure response, provide a factory service:

use Gracious\FeatureFlagBundle\Exception\ExceptionFactoryInterface;
use Gracious\FeatureFlagBundle\Flag\Flag;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

final class AccessDeniedExceptionFactory implements ExceptionFactoryInterface
{
    public function create(Flag $flag, bool $required): \Throwable
    {
        return new AccessDeniedHttpException(
            sprintf('Feature "%s" gate failed.', $flag->name),
        );
    }
}
gracious_feature_flag:
    exception:
        factory: App\FeatureFlag\AccessDeniedExceptionFactory

Alternatively set exception.class to any class with a (string $name, int $statusCode) constructor. A factory always wins over class.

REST endpoint

The endpoint is read-only and opt-in. Import the routes to enable it:

# config/routes/gracious_feature_flag.yaml
feature_flags:
    resource: '@GraciousFeatureFlagBundle/config/routes.php'
    prefix: /_feature-flags
    trailing_slash_on_root: false
Method Path Description
GET /_feature-flags list all flags (default manager)
GET /_feature-flags/{name} read a single flag

Both accept ?manager=<name>. Unknown flag or manager returns 404.

curl http://localhost/_feature-flags
# [{"name":"new_checkout","enabled":true,"description":"New checkout flow"}]

trailing_slash_on_root: false keeps the list route at /_feature-flags so the request returns 200 directly instead of a 301 redirect to /_feature-flags/.

Disabling the endpoint. Set api.enabled: false. Both routes then return 404 even if still imported, a hard kill switch independent of routing.

Security: these endpoints expose flag names and states and are not protected by the bundle. Restrict the prefix to a dev/internal firewall or guard the import with access control before exposing it in production.

统计信息

  • 总下载量: 0
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 0
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-06-02

承接程序开发

PHP开发

VUE

Vue开发

前端开发

小程序开发

公众号开发

系统定制

数据库设计

云部署

网站建设

安全加固