PHPackages                             azaharizaman/nexus-feature-flags - 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. azaharizaman/nexus-feature-flags

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

azaharizaman/nexus-feature-flags
================================

Production-grade feature flag management with context-based evaluation, percentage rollout, and kill switches

v0.1.0-alpha1(1mo ago)02↓100%2MITPHPPHP ^8.3

Since May 5Pushed 1mo agoCompare

[ Source](https://github.com/azaharizaman/nexus-feature-flags)[ Packagist](https://packagist.org/packages/azaharizaman/nexus-feature-flags)[ RSS](/packages/azaharizaman-nexus-feature-flags/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (2)Versions (2)Used By (2)

Nexus\\FeatureFlags
===================

[](#nexusfeatureflags)

[![Latest Version](https://camo.githubusercontent.com/7fa1cc79930d5575332915313ca0e567e11b06f3c561d646314aa6de2dc3971d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f617a61686172697a616d616e2f6e657875732d666561747572652d666c6167732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/azaharizaman/nexus-feature-flags)[![Total Downloads](https://camo.githubusercontent.com/77c7ee59244cb11da717d693514089f4084d89e6a57d63c9352aa7823112a682/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f617a61686172697a616d616e2f6e657875732d666561747572652d666c6167732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/azaharizaman/nexus-feature-flags)[![License](https://camo.githubusercontent.com/cd0c92fde312116e820e6b30f85f6b69a9f6352e31deafca0e2054f7d27d1b4b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f617a61686172697a616d616e2f6e657875732d666561747572652d666c6167732e7376673f7374796c653d666c61742d737175617265)](LICENSE)

Production-grade feature flag management with context-based evaluation, percentage rollout, tenant inheritance, and kill switches. Framework-agnostic pure PHP 8.3+ package designed for Laravel, Symfony, Slim, and vanilla PHP applications.

Features
--------

[](#features)

- 🎯 **Context-Based Evaluation** - 5 strategies: System-Wide, Percentage Rollout, Tenant List, User List, Custom
- 🔐 **Fail-Closed Security** - Flags default to disabled when not found
- 🏢 **Tenant Inheritance** - Tenant-specific flags override global defaults
- 🚦 **Kill Switches** - Force ON/OFF overrides for emergency control
- ⚡ **Performance Optimized** - Request-level memoization, bulk evaluation API
- 🔍 **Checksum Validation** - Prevents stale cache serving
- 📊 **Observability Ready** - Optional monitoring and audit logging integration
- 🧪 **100% Type-Safe** - Strict types, native enums, immutable value objects

Installation
------------

[](#installation)

### For Laravel

[](#for-laravel)

```
composer require azaharizaman/nexus-feature-flags
```

The service provider will be auto-discovered. Publish the migration:

```
php artisan vendor:publish --tag=feature-flags-migrations
php artisan migrate
```

### For Symfony

[](#for-symfony)

```
composer require azaharizaman/nexus-feature-flags
```

Register services in `config/services.yaml`:

```
services:
    Nexus\FeatureFlags\Contracts\FlagRepositoryInterface:
        class: Your\Custom\FlagRepository

    Nexus\FeatureFlags\Contracts\FeatureFlagManagerInterface:
        class: Nexus\FeatureFlags\Services\FeatureFlagManager
        arguments:
            - '@Nexus\FeatureFlags\Contracts\FlagRepositoryInterface'
            - '@Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator'
            - '@logger'
```

### For Vanilla PHP

[](#for-vanilla-php)

```
composer require azaharizaman/nexus-feature-flags
```

```
use Nexus\FeatureFlags\Services\FeatureFlagManager;
use Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator;
use Nexus\FeatureFlags\Core\Repository\InMemoryFlagRepository;
use Psr\Log\NullLogger;

$repository = new InMemoryFlagRepository();
$evaluator = new DefaultFlagEvaluator(new PercentageHasher());
$manager = new FeatureFlagManager($repository, $evaluator, new NullLogger());
```

Quick Start
-----------

[](#quick-start)

### Basic Usage

[](#basic-usage)

```
use Nexus\FeatureFlags\Contracts\FeatureFlagManagerInterface;

class MyController
{
    public function __construct(
        private readonly FeatureFlagManagerInterface $flags
    ) {}

    public function index(): Response
    {
        if ($this->flags->isEnabled('new_dashboard')) {
            return $this->renderNewDashboard();
        }

        return $this->renderOldDashboard();
    }
}
```

### Context-Based Evaluation

[](#context-based-evaluation)

```
use Nexus\FeatureFlags\ValueObjects\EvaluationContext;

$context = new EvaluationContext(
    tenantId: 'tenant-123',
    userId: 'user-456',
    customAttributes: ['plan' => 'premium']
);

if ($this->flags->isEnabled('advanced_analytics', $context)) {
    // Show premium feature
}
```

### Bulk Evaluation (Prevents N+1 Queries)

[](#bulk-evaluation-prevents-n1-queries)

```
$context = EvaluationContext::fromArray([
    'tenant_id' => 'tenant-123',
    'user_id' => 'user-456',
]);

$flags = $this->flags->evaluateMany([
    'new_dashboard',
    'advanced_analytics',
    'beta_features',
], $context);

// Returns: ['new_dashboard' => true, 'advanced_analytics' => false, 'beta_features' => true]
```

Flag Strategies
---------------

[](#flag-strategies)

### 1. System-Wide

[](#1-system-wide)

Enabled/disabled for all users globally.

```
use Nexus\FeatureFlags\ValueObjects\FlagDefinition;
use Nexus\FeatureFlags\Enums\FlagStrategy;

$flag = new FlagDefinition(
    name: 'maintenance_mode',
    enabled: true,
    strategy: FlagStrategy::SYSTEM_WIDE,
    value: null
);
```

### 2. Percentage Rollout

[](#2-percentage-rollout)

Gradually roll out to a percentage of users based on stable identifier.

```
$flag = new FlagDefinition(
    name: 'new_checkout',
    enabled: true,
    strategy: FlagStrategy::PERCENTAGE_ROLLOUT,
    value: 25 // 25% of users
);

// Requires stable identifier in context
$context = new EvaluationContext(userId: 'user-123');
$enabled = $manager->isEnabled('new_checkout', $context);
```

### 3. Tenant List

[](#3-tenant-list)

Enabled only for specific tenants.

```
$flag = new FlagDefinition(
    name: 'premium_module',
    enabled: true,
    strategy: FlagStrategy::TENANT_LIST,
    value: ['tenant-abc', 'tenant-xyz']
);

$context = new EvaluationContext(tenantId: 'tenant-abc');
$enabled = $manager->isEnabled('premium_module', $context); // true
```

### 4. User List

[](#4-user-list)

Enabled only for specific users.

```
$flag = new FlagDefinition(
    name: 'beta_tester_access',
    enabled: true,
    strategy: FlagStrategy::USER_LIST,
    value: ['user-alice', 'user-bob']
);
```

### 5. Custom Evaluator (Advanced)

[](#5-custom-evaluator-advanced)

Use custom business logic for complex targeting.

```
use Nexus\FeatureFlags\Contracts\CustomEvaluatorInterface;
use Nexus\FeatureFlags\ValueObjects\EvaluationContext;

class PremiumMalaysianUsersEvaluator implements CustomEvaluatorInterface
{
    public function evaluate(EvaluationContext $context): bool
    {
        $plan = $context->customAttributes['plan'] ?? null;
        $country = $context->customAttributes['country'] ?? null;

        return $plan === 'premium' && $country === 'MY';
    }
}

$flag = new FlagDefinition(
    name: 'malaysia_premium_features',
    enabled: true,
    strategy: FlagStrategy::CUSTOM,
    value: PremiumMalaysianUsersEvaluator::class
);
```

Override Precedence (Kill Switches)
-----------------------------------

[](#override-precedence-kill-switches)

Force flags ON or OFF regardless of strategy for emergency control.

```
use Nexus\FeatureFlags\Enums\FlagOverride;

// Emergency kill switch - disables feature even if enabled=true
$flag = new FlagDefinition(
    name: 'problematic_feature',
    enabled: true,
    strategy: FlagStrategy::SYSTEM_WIDE,
    value: null,
    override: FlagOverride::FORCE_OFF
);

$manager->isEnabled('problematic_feature'); // Always returns false

// Force enable during testing
$flag = new FlagDefinition(
    name: 'test_feature',
    enabled: false,
    strategy: FlagStrategy::SYSTEM_WIDE,
    value: null,
    override: FlagOverride::FORCE_ON
);

$manager->isEnabled('test_feature'); // Always returns true
```

Tenant Inheritance
------------------

[](#tenant-inheritance)

Tenant-specific flags automatically override global defaults.

```
// Global default: disabled for all tenants
$globalFlag = new FlagDefinition(
    name: 'new_reporting',
    enabled: false,
    strategy: FlagStrategy::SYSTEM_WIDE,
    value: null
    // tenant_id: null
);

// Enable only for tenant-123
$tenantFlag = new FlagDefinition(
    name: 'new_reporting',
    enabled: true,
    strategy: FlagStrategy::SYSTEM_WIDE,
    value: null
    // tenant_id: 'tenant-123'
);

// Tenant-123 sees enabled, all others see disabled
$context = new EvaluationContext(tenantId: 'tenant-123');
$manager->isEnabled('new_reporting', $context); // true

$context = new EvaluationContext(tenantId: 'tenant-456');
$manager->isEnabled('new_reporting', $context); // false (uses global)
```

Name Validation
---------------

[](#name-validation)

Flag names must follow strict pattern for consistency and safety:

- **Pattern:** `/^[a-z0-9_\.]{1,100}$/`
- **Valid:** `new_feature`, `module.analytics`, `beta_v2.checkout`
- **Invalid:** `NewFeature` (uppercase), `feature-name` (hyphens), `very_long_name...` (&gt;100 chars)

```
// Valid
new FlagDefinition(name: 'analytics.dashboard.v2', ...);

// Throws InvalidFlagDefinitionException
new FlagDefinition(name: 'Invalid-Name', ...);
```

Testing
-------

[](#testing)

```
# Run all tests
composer test

# Run with coverage
composer test -- --coverage-html coverage

# Run only unit tests
composer test -- --testsuite=Unit

# Run only integration tests
composer test -- --testsuite=Integration
```

Framework Integration Examples
------------------------------

[](#framework-integration-examples)

### Laravel Setup

[](#laravel-setup)

After installation, the `FeatureFlagServiceProvider` auto-registers. Configure in `config/feature-flags.php`:

```
return [
    'cache_store' => env('FEATURE_FLAGS_CACHE_STORE', 'redis'),
    'cache_ttl' => env('FEATURE_FLAGS_CACHE_TTL', 300), // 5 minutes
    'default_if_not_found' => env('FEATURE_FLAGS_DEFAULT_IF_NOT_FOUND', false),
    'enable_monitoring' => env('FEATURE_FLAGS_ENABLE_MONITORING', true),
];
```

**API Usage:**

```
// In controllers
public function __construct(
    private readonly FeatureFlagManagerInterface $flags
) {}

public function show(Request $request): JsonResponse
{
    $context = [
        'tenantId' => $request->user()->tenant_id,
        'userId' => $request->user()->id,
    ];

    if ($this->flags->isEnabled('premium.analytics', $context)) {
        return response()->json(['data' => $this->getPremiumAnalytics()]);
    }

    return response()->json(['data' => $this->getBasicAnalytics()]);
}
```

**Blade Directives (Optional Custom Helper):**

```
// In AppServiceProvider
Blade::directive('featureFlag', function ($expression) {
    return "";
});

Blade::directive('endfeatureFlag', function () {
    return "";
});
```

```
@featureFlag('new.ui')
    New UI!
@endfeatureFlag
@else
    Legacy UI
@endif
```

### Symfony Setup

[](#symfony-setup)

**services.yaml:**

```
services:
    # Repository (choose one)
    Nexus\FeatureFlags\Contracts\FlagRepositoryInterface:
        class: App\FeatureFlags\DoctrineFlagRepository
        arguments:
            - '@doctrine.orm.entity_manager'

    # Cache adapter
    Nexus\FeatureFlags\Contracts\FlagCacheInterface:
        class: App\FeatureFlags\SymfonyCacheAdapter
        arguments:
            - '@cache.app'

    # Core services
    Nexus\FeatureFlags\Core\Engine\PercentageHasher: ~

    Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator:
        arguments:
            - '@Nexus\FeatureFlags\Core\Engine\PercentageHasher'

    Nexus\FeatureFlags\Services\FeatureFlagManager:
        arguments:
            - '@Nexus\FeatureFlags\Contracts\FlagRepositoryInterface'
            - '@Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator'
            - '@logger'

    # Alias for type-hinting
    Nexus\FeatureFlags\Contracts\FeatureFlagManagerInterface:
        alias: Nexus\FeatureFlags\Services\FeatureFlagManager
```

**Controller Usage:**

```
use Nexus\FeatureFlags\Contracts\FeatureFlagManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class DashboardController extends AbstractController
{
    public function __construct(
        private readonly FeatureFlagManagerInterface $flags
    ) {}

    #[Route('/dashboard')]
    public function index(RequestStack $requestStack): Response
    {
        $context = [
            'userId' => $this->getUser()?->getId(),
            'tenantId' => $requestStack->getCurrentRequest()?->attributes->get('tenant_id'),
        ];

        return $this->render('dashboard/index.html.twig', [
            'use_new_ui' => $this->flags->isEnabled('dashboard.v2', $context),
        ]);
    }
}
```

**Twig Extension (Optional):**

```
namespace App\Twig;

use Nexus\FeatureFlags\Contracts\FeatureFlagManagerInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class FeatureFlagExtension extends AbstractExtension
{
    public function __construct(
        private readonly FeatureFlagManagerInterface $flags
    ) {}

    public function getFunctions(): array
    {
        return [
            new TwigFunction('feature_enabled', [$this, 'isEnabled']),
        ];
    }

    public function isEnabled(string $flagName, array $context = []): bool
    {
        return $this->flags->isEnabled($flagName, $context);
    }
}
```

```
{% if feature_enabled('new.checkout') %}
    Enhanced Checkout
{% else %}
    Classic Checkout
{% endif %}
```

### Slim Framework Setup

[](#slim-framework-setup)

```
use Nexus\FeatureFlags\Services\FeatureFlagManager;
use Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator;
use Nexus\FeatureFlags\Core\Repository\InMemoryFlagRepository;
use Psr\Container\ContainerInterface;
use Slim\Factory\AppFactory;

$container = new \DI\Container();

// Register feature flag services
$container->set(FlagRepositoryInterface::class, function() {
    return new InMemoryFlagRepository(); // Or your custom implementation
});

$container->set(FeatureFlagManagerInterface::class, function(ContainerInterface $c) {
    return new FeatureFlagManager(
        $c->get(FlagRepositoryInterface::class),
        new DefaultFlagEvaluator(new PercentageHasher()),
        $c->get(LoggerInterface::class)
    );
});

AppFactory::setContainer($container);
$app = AppFactory::create();

// Use in routes
$app->get('/dashboard', function (Request $request, Response $response) use ($container) {
    $flags = $container->get(FeatureFlagManagerInterface::class);

    $context = [
        'userId' => $request->getAttribute('user_id'),
    ];

    if ($flags->isEnabled('beta.features', $context)) {
        return $response->withJson(['version' => 'beta']);
    }

    return $response->withJson(['version' => 'stable']);
});
```

### Standalone PHP Setup

[](#standalone-php-setup)

```
