Attribute Dispatchers
Attribute dispatchers provide a declarative way to define feature flags directly in your class code using PHP 8 attributes. This approach makes it easy to see which methods are controlled by feature flags without cluttering your dispatcher configuration.
Defining the Attribute
Use the #[FeatureFlagAttribute] attribute to mark methods that should be executed based on feature flag conditions:
use ByJG\FeatureFlag\Attributes\FeatureFlagAttribute;
class NotificationService
{
#[FeatureFlagAttribute('email-notifications', 'enabled')]
public function sendEmail(string $to, string $subject): void
{
// This method executes only when 'email-notifications' flag is 'enabled'
echo "Sending email to {$to}\n";
}
#[FeatureFlagAttribute('sms-notifications')]
public function sendSMS(string $phone): void
{
// This method executes when 'sms-notifications' flag is set (any value)
echo "Sending SMS to {$phone}\n";
}
#[FeatureFlagAttribute('push-notifications', 'beta')]
public function sendPushNotification(string $deviceId): void
{
// This method executes only when 'push-notifications' flag is 'beta'
echo "Sending push notification\n";
}
}
Attribute Syntax
The FeatureFlagAttribute accepts two parameters:
#[FeatureFlagAttribute(string $featureFlag, ?string $featureValue = null)]
$featureFlag(required): The name of the feature flag to check$featureValue(optional): The specific value the flag must have. If omitted (null), any value will match. Any string, including'0'and'', must match exactly
Adding to the Dispatcher
Use the addClass() method to automatically register all attributed methods:
use ByJG\FeatureFlag\FeatureFlags;
use ByJG\FeatureFlag\FeatureFlagDispatcher;
// Define feature flags
FeatureFlags::addFlag('email-notifications', 'enabled');
FeatureFlags::addFlag('sms-notifications', 'on');
// Create dispatcher
$dispatcher = new FeatureFlagDispatcher();
// Add the class - all methods with FeatureFlagAttribute will be registered
$dispatcher->addClass(NotificationService::class);
// Dispatch with arguments
How It Works
When you call addClass(), the dispatcher:
- Uses reflection to scan the class for methods with
FeatureFlagAttribute - Creates a
StaticMethodHandlerfor each attributed method - Registers the handler with the appropriate selector
The dispatcher automatically handles both static and instance methods. For instance methods, a new instance of the class is created automatically.
Static vs Instance Methods
Both static and instance methods are supported:
class FeatureService
{
#[FeatureFlagAttribute('feature-a')]
public static function staticHandler(): void
{
// Called as FeatureService::staticHandler()
echo "Static method executed\n";
}
#[FeatureFlagAttribute('feature-b')]
public function instanceHandler(): void
{
// A new instance is created and this method is called
echo "Instance method executed\n";
}
}
Passing Arguments
Attributed methods receive the same arguments passed to dispatch():
class LogService
{
#[FeatureFlagAttribute('detailed-logging')]
public function logDetails(string $message, array $context): void
{
echo "Logging: {$message} with context: " . json_encode($context) . "\n";
}
}
// Usage
$dispatcher->addClass(LogService::class);
$dispatcher->dispatch('User logged in', ['user_id' => 123, 'ip' => '192.168.1.1']);
Multiple Flags in One Class
You can have multiple methods with different feature flags in the same class:
class PaymentService
{
#[FeatureFlagAttribute('stripe-payment')]
public function processStripe(float $amount): void
{
echo "Processing Stripe payment: \${$amount}\n";
}
#[FeatureFlagAttribute('paypal-payment')]
public function processPayPal(float $amount): void
{
echo "Processing PayPal payment: \${$amount}\n";
}
#[FeatureFlagAttribute('crypto-payment', 'enabled')]
public function processCrypto(float $amount): void
{
echo "Processing Crypto payment: \${$amount}\n";
}
}
Limitations
- Only public methods can be decorated with
FeatureFlagAttribute - Methods must be either static or have a parameterless constructor for their class
- Private and protected methods are ignored, static or not
Best Practices
- Group Related Handlers: Keep feature flag handlers in dedicated service classes
- Clear Naming: Use descriptive method names that indicate what feature they control
- Documentation: Add docblocks to explain what each feature flag controls
- Testing: Test both when flags are enabled and disabled
See Also
- Handler Interface - Create custom handlers
- FeatureFlagSelector - Manual selector configuration
- Passing Arguments - How arguments are passed to handlers