PHP code example of turtlemilitia / laravel-featureflags

1. Go to this page and download the library: Download turtlemilitia/laravel-featureflags 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/ */

    

turtlemilitia / laravel-featureflags example snippets


use FeatureFlags\Facades\Feature;

if (Feature::active('dark-mode')) {
    // Show dark mode UI
}

use FeatureFlags\Facades\Feature;

// Check if a flag is active (boolean)
if (Feature::active('dark-mode')) {
    // Show dark mode UI
}

// Get flag value (supports string, number, JSON)
$limit = Feature::value('api-rate-limit');

// Get all flags
$flags = Feature::all();

// Monitor critical code paths (tracks errors automatically)
$result = Feature::monitor('new-payment-flow', fn ($enabled) =>
    $enabled ? $this->newPayment() : $this->legacyPayment()
);

// Track conversions for A/B analysis
Feature::trackConversion('purchase', $user, ['revenue' => 99.99]);

use FeatureFlags\Contracts\HasFeatureFlagContext;

class User extends Authenticatable implements HasFeatureFlagContext
{
    public function toFeatureFlagContext(): array
    {
        return [
            'id' => $this->id,
            'email' => $this->email,
            'plan' => $this->subscription?->plan,
        ];
    }
}

// Team, Organization, etc.
Feature::active('premium-feature', $team);

// Array shorthand
Feature::active('new-checkout', [
    'id' => 'user-123',
    'plan' => 'pro',
]);

// Context object
$context = new Context('user-123', ['plan' => 'pro']);
Feature::active('new-checkout', $context);

public function toFeatureFlagContext(): array
{
    return [
        'id' => $this->id,
        'subscription' => [
            'plan' => [
                'name' => $this->subscription?->plan?->name,
                'tier' => $this->subscription?->plan?->tier,
            ],
            'status' => $this->subscription?->status,
        ],
    ];
}



namespace App\FeatureFlags;

use FeatureFlags\Contracts\ResolvesVersion;

class VersionResolver implements ResolvesVersion
{
    public function resolve(): array
    {
        return [
            // From client request header
            'client_version' => request()->header('X-App-Version'),

            // With fallback logic
            'app_version' => request()->header('X-App-Version')
                ?? config('app.version'),
        ];
    }
}

// config/featureflags.php
'context' => [
    'version_resolver' => \App\FeatureFlags\VersionResolver::class,
],

// User sees their assigned variant automatically
$buttonColor = Feature::value('checkout-button-color');
// Returns 'blue', 'red', or 'green' based on user's bucket

// User sees the checkout button (variant: 'red')
$color = Feature::value('checkout-button-color');

// Later in the same request or session...
Feature::trackConversion('purchase', $user, ['revenue' => 99.99]);
// Conversion is automatically attributed to 'checkout-button-color' = 'red'

Feature::sync();
Feature::flush(); // Clear cache

'fallback' => [
    'behavior' => 'cache', // 'cache', 'default', or 'exception'
    'default_value' => false, // Used when behavior is 'default'
],

Feature::trackConversion('purchase', $user, ['revenue' => 99.99]);

Feature::trackConversion('purchase', $user, [
    'revenue' => 99.99,
], flagKey: 'checkout-button-color', flagValue: 'red');

// Laravel 11+ (bootstrap/providers.php)
return [
    // ...
    FeatureFlags\Integrations\ErrorTrackingServiceProvider::class,
];

// Laravel 10 (config/app.php)
'providers' => [
    // ...
    FeatureFlags\Integrations\ErrorTrackingServiceProvider::class,
],

$result = Feature::monitor('new-payment-processor', function ($isEnabled) {
    if ($isEnabled) {
        return $this->processWithStripe();
    }
    return $this->processWithLegacy();
});

try {
    if (Feature::active('risky-feature')) {
        $this->riskyOperation();
    }
} catch (\Exception $e) {
    Feature::trackError('risky-feature', $e, ['custom' => 'metadata']);
    throw $e;
}

'error_tracking' => [
    'enabled' => true,
    'skip_exceptions' => [
        \Symfony\Component\HttpKernel\Exception\NotFoundHttpException::class,
        \Illuminate\Validation\ValidationException::class,
        \Illuminate\Auth\AuthenticationException::class,
    ],
],

'local' => [
    'enabled' => true,
    'flags' => [
        'new-checkout' => true,
        'dark-mode' => false,
        'api-rate-limit' => 100,
        'beta-features' => ['value' => true, 'rollout' => 25],
    ],
],

use FeatureFlags\Facades\Feature;

// Simple boolean flag
Feature::shouldReceive('active')
    ->with('my-flag')
    ->andReturn(true);

// Flag with context
Feature::shouldReceive('active')
    ->with('premium-feature', Mockery::any())
    ->andReturn(false);

// Value flags
Feature::shouldReceive('value')
    ->with('api-rate-limit')
    ->andReturn(100);

public function test_pro_users_see_feature(): void
{
    config(['featureflags.local.enabled' => true]);
    config(['featureflags.local.flags' => [
        'new-dashboard' => [
            'value' => false,
            'rules' => [
                [
                    'conditions' => [
                        ['trait' => 'plan', 'operator' => 'equals', 'value' => 'pro'],
                    ],
                    'value' => true,
                ],
            ],
        ],
    ]]);

    $proUser = new Context('user-1', ['plan' => 'pro']);
    $freeUser = new Context('user-2', ['plan' => 'free']);

    $this->assertTrue(Feature::active('new-dashboard', $proUser));
    $this->assertFalse(Feature::active('new-dashboard', $freeUser));
}

public function test_rollout_is_consistent(): void
{
    $context = new Context('user-123', []);

    $first = Feature::active('gradual-rollout', $context);
    $second = Feature::active('gradual-rollout', $context);

    // Same context always gets same result
    $this->assertEquals($first, $second);
}

'events' => [
    'enabled' => true,
    'dispatch' => [
        'flag_evaluated' => true,
        'flag_sync_completed' => true,
        'telemetry_flushed' => true,
    ],
],

'telemetry' => [
    'sample_rate' => 0.1, // Track 10% of evaluations
],

'telemetry' => [
    'rate_limit' => [
        'enabled' => true,
        'max_flushes_per_minute' => 60,
    ],
],

'telemetry' => [
    'async' => env('FEATUREFLAGS_TELEMETRY_ASYNC', false),
    'queue' => env('FEATUREFLAGS_TELEMETRY_QUEUE', null), // null = default queue
],

use FeatureFlags\Facades\Feature;

// Flags work immediately (bucketing happens, telemetry is held)
if (Feature::active('new-checkout')) {
    // Show new checkout
}

// When user accepts analytics/cookies:
Feature::grantConsent();  // Flushes held events, sets consent cookie

// If user declines:
Feature::discardHeldTelemetry();  // Clears queued events without sending

// To revoke consent later:
Feature::revokeConsent();  // Future telemetry will be held again

// Check current state:
Feature::isHoldingTelemetry();  // true if holding without consent

'telemetry' => [
    'hold_until_consent' => env('FEATUREFLAGS_HOLD_UNTIL_CONSENT', false),
    'consent_ttl_days' => env('FEATUREFLAGS_CONSENT_TTL_DAYS', 365),
],
bash
php artisan vendor:publish --tag=featureflags-config
bash
php artisan featureflags:sync
bash
php artisan featureflags:dump --format=php|json|yaml --output=flags.php
bash
php artisan featureflags:warm --retry=3 --retry-delay=2