PHP code example of azaharizaman / nexus-feature-flags

1. Go to this page and download the library: Download azaharizaman/nexus-feature-flags library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

azaharizaman / nexus-feature-flags example snippets


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());

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();
    }
}

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
}

$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]

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

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

$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);

$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

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

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
);

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

// 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)

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

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

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),
];

// 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()]);
}

// In AppServiceProvider
Blade::directive('featureFlag', function ($expression) {
    return " if(app(FeatureFlagManagerInterface::class)->isEnabled($expression)): 

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),
        ]);
    }
}

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);
    }
}

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']);
});



exus\FeatureFlags\Services\FeatureFlagManager;
use Nexus\FeatureFlags\Core\Engine\DefaultFlagEvaluator;
use Nexus\FeatureFlags\Core\Engine\PercentageHasher;
use Nexus\FeatureFlags\Core\Repository\InMemoryFlagRepository;
use Nexus\FeatureFlags\ValueObjects\FlagDefinition;
use Nexus\FeatureFlags\Enums\FlagStrategy;
use Psr\Log\NullLogger;

// Setup repository and add flags
$repository = new InMemoryFlagRepository();

$repository->save(FlagDefinition::create(
    name: 'new.feature',
    enabled: true,
    strategy: FlagStrategy::PERCENTAGE_ROLLOUT,
    value: 50 // 50% rollout
));

// Create manager
$manager = new FeatureFlagManager(
    repository: $repository,
    evaluator: new DefaultFlagEvaluator(new PercentageHasher()),
    logger: new NullLogger()
);

// Evaluate flags
$context = ['userId' => 'user-12345'];

if ($manager->isEnabled('new.feature', $context)) {
    echo "You're in the 50% rollout group!\n";
} else {
    echo "Not yet enabled for you.\n";
}

use Nexus\FeatureFlags\Contracts\FlagAuditChangeInterface;
use Nexus\FeatureFlags\Enums\AuditAction;
use Nexus\AuditLogger\Contracts\AuditLogRepositoryInterface;

// Application layer implementation
final readonly class FeatureFlagAuditLogger implements FlagAuditChangeInterface
{
    public function __construct(
        private AuditLogRepositoryInterface $auditLogger,
        private TenantContextInterface $tenantContext
    ) {}

    public function recordChange(
        string $flagName,
        AuditAction $action,
        ?string $userId,
        ?array $before,
        ?array $after,
        array $metadata = []
    ): void {
        $this->auditLogger->create([
            'log_name' => 'feature_flags',
            'subject_type' => 'feature_flag',
            'subject_id' => $flagName,
            'causer_type' => 'user',
            'causer_id' => $userId,
            'event' => $action->value,
            'description' => $action->getDescription(),
            'properties' => [
                'before' => $before,
                'after' => $after,
                ...$metadata,
            ],
            'level' => $action->isCritical() ? 4 : 2, // Critical = 4, Medium = 2
            'tenant_id' => $this->tenantContext->getCurrentTenantId(),
        ]);
    }

    public function recordBatchChange(
        AuditAction $action,
        ?string $userId,
        array $changes,
        array $metadata = []
    ): void {
        // Generate batch ID using your preferred method:
        // - Symfony: (new \Symfony\Component\Uid\Ulid())->__toString()
        // - ramsey/uuid: (string) \Ramsey\Uuid\Uuid::uuid7()
        // - Laravel: (string) Str::ulid()
        $batchId = $this->generateBatchId();
        
        foreach ($changes as $flagName => $change) {
            $this->recordChange(
                $flagName,
                $action,
                $userId,
                $change['before'],
                $change['after'],
                [...$metadata, 'batch_id' => $batchId]
            );
        }
    }

    private function generateBatchId(): string
    {
        // Implement using your preferred ULID/UUID library
        return (new \Symfony\Component\Uid\Ulid())->__toString();
    }
}

use Nexus\FeatureFlags\Contracts\FlagAuditQueryInterface;
use Nexus\FeatureFlags\Contracts\FlagAuditRecordInterface;
use Nexus\FeatureFlags\Enums\AuditAction;
use Nexus\EventStream\Contracts\EventStoreInterface;

// Application layer implementation
final readonly class FeatureFlagAuditQuery implements FlagAuditQueryInterface
{
    public function __construct(
        private EventStoreInterface $eventStore
    ) {}

    public function getHistory(
        string $flagName,
        ?string $tenantId = null,
        int $limit = 100,
        int $offset = 0
    ): array {
        return $this->eventStore->query(
            filters: [
                'aggregate_id' => ['operator' => '=', 'value' => "flag:{$flagName}"],
            ],
            inFilters: [],
            orderByField: 'occurred_at',
            orderDirection: 'desc',
            limit: $limit,
            cursorData: null
        );
    }

    public function getStateAt(
        string $flagName,
        DateTimeImmutable $timestamp,
        ?string $tenantId = null
    ): ?array {
        // Replay events up to timestamp to reconstruct state
        $events = $this->eventStore->query(
            filters: [
                'aggregate_id' => ['operator' => '=', 'value' => "flag:{$flagName}"],
                'occurred_at' => ['operator' => '<=', 'value' => $timestamp->format('Y-m-d H:i:s')],
            ],
            inFilters: [],
            orderByField: 'occurred_at',
            orderDirection: 'asc',
            limit: 10000
        );
        
        if (empty($events)) {
            return null;
        }
        
        // Reconstruct state by replaying events
        return $this->reconstructState($events);
    }

    public function getCriticalChanges(
        ?string $tenantId = null,
        ?DateTimeImmutable $since = null,
        int $limit = 500
    ): array {
        $filters = [];
        
        if ($since !== null) {
            $filters['occurred_at'] = ['operator' => '>=', 'value' => $since->format('Y-m-d H:i:s')];
        }
        
        $criticalActions = [
            AuditAction::FORCE_DISABLED->value,
            AuditAction::FORCE_ENABLED->value,
            AuditAction::DELETED->value,
            AuditAction::OVERRIDE_CHANGED->value,
        ];
        
        return $this->eventStore->query(
            filters: $filters,
            inFilters: ['event_type' => $criticalActions],
            orderByField: 'occurred_at',
            orderDirection: 'desc',
            limit: $limit
        );
    }
    
    // ... other methods
}

use Nexus\FeatureFlags\Services\AuditableFlagRepository;

// Wrap your repository for automatic audit logging
$auditableRepo = new AuditableFlagRepository(
    repository: $baseRepository,
    auditChange: $auditChangeLogger,
    userId: $currentUser->getId()
);

// Set tenant context
$auditableRepo = $auditableRepo->withTenantId($tenantId);

// Now all save/delete operations are automatically audited
$auditableRepo->save($flag); // Records CREATED or appropriate action
$auditableRepo->delete('old_flag', $tenantId); // Records DELETED

// Check if audit capabilities are configured
if ($manager->hasAuditChange()) {
    // Audit change logging is available
}

if ($manager->hasAuditQuery()) {
    // Historical queries are available
    $query = $manager->getAuditQuery();
    
    // Get flag history
    $history = $query->getHistory('payment_v2', 'tenant-123');
    
    // Compliance audit: What was the state during an incident?
    $stateAtIncident = $query->getStateAt(
        'payment_v2',
        new DateTimeImmutable('2024-11-15 14:30:00'),
        'tenant-123'
    );
    
    // Get all critical changes this month
    $criticalChanges = $query->getCriticalChanges(
        tenantId: 'tenant-123',
        since: new DateTimeImmutable('first day of this month')
    );
}

// AppServiceProvider.php
public function register(): void
{
    // Register audit change logger (uses Nexus\AuditLogger)
    $this->app->singleton(FlagAuditChangeInterface::class, function ($app) {
        return new FeatureFlagAuditLogger(
            $app->make(AuditLogRepositoryInterface::class),
            $app->make(TenantContextInterface::class)
        );
    });

    // Register audit query (uses Nexus\EventStream)
    $this->app->singleton(FlagAuditQueryInterface::class, function ($app) {
        return new FeatureFlagAuditQuery(
            $app->make(EventStoreInterface::class)
        );
    });

    // Register manager with audit support
    $this->app->singleton(FeatureFlagManagerInterface::class, function ($app) {
        return new FeatureFlagManager(
            $app->make(FlagRepositoryInterface::class),
            $app->make(FlagEvaluatorInterface::class),
            $app->make(LoggerInterface::class),
            $app->make(FlagAuditChangeInterface::class), // Optional
            $app->make(FlagAuditQueryInterface::class)   // Optional
        );
    });
}
bash
php artisan vendor:publish --tag=feature-flags-migrations
php artisan migrate