Skip to main content

Advanced Usage

Learn advanced patterns and features of the PHP SDK.

Custom Context​

Provide custom context for feature evaluation:

use Toggly\FeatureManagement\Core\FeatureManager;

// Prefer EvalContext-shaped keys (see FeatureManager docblock)
$context = [
'identity' => '123',
'groups' => ['premium'],
'claims' => ['email' => '[email protected]'],
'request' => [
'userAgent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'acceptLanguage' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? null,
'country' => null, // set if you have geo
],
'traits' => [
'plan' => 'enterprise',
'region' => 'us-east',
],
];

if ($featureManager->isEnabled('my-feature', $context)) {
// Feature evaluation uses the provided context
}

Feature State Service​

Use the feature state service to react to feature changes:

use Toggly\FeatureManagement\Core\FeatureStateService;

$stateService = new FeatureStateService();

// Register callback when feature turns on
$stateService->whenFeatureTurnsOn('my-feature', function () {
// Clear cache, send notification, etc.
cache()->forget('feature-data');
event(new FeatureEnabled('my-feature'));
});

// Register callback when feature turns off
$stateService->whenFeatureTurnsOff('my-feature', function () {
// Cleanup, notify users, etc.
cleanupFeatureData();
});

// Listen for any definition changes
$stateService->whenDefinitionsChange(function (array $changedFeatures) {
foreach ($changedFeatures as $featureName) {
logger()->info("Feature {$featureName} definitions changed");
}
});

// Unregister callbacks
$stateService->unregisterFeatureStateChange('my-feature');
$stateService->unregisterDefinitionsChange();

Metrics Registry​

Register custom metric sources:

use Toggly\FeatureManagement\Core\MetricsRegistryService;

$registry = new MetricsRegistryService();

// Register a measurement callback
$registry->registerMeasurements('checkout-time', function () {
return [
['value' => 2.5, 'tags' => ['feature' => 'new-checkout']],
['value' => 3.1, 'tags' => ['feature' => 'old-checkout']],
];
});

// Register an observation callback
$registry->registerObservations('button-clicks', function () {
return [
['tags' => ['button' => 'checkout', 'page' => 'cart']],
['tags' => ['button' => 'subscribe', 'page' => 'home']],
];
});

// Register a counter callback
$registry->registerCounters('page-views', function () {
return [
['value' => 10, 'tags' => ['page' => 'home']],
['value' => 5, 'tags' => ['page' => 'about']],
];
});

// Metrics service will automatically collect from registry
$metricsService = new MetricsService($settings, $httpClient, $registry);

Secure Feature Authorization​

Implement custom authorization for secure features:

use Toggly\FeatureManagement\Contracts\FeatureAuthorizationServiceInterface;

class RoleBasedAuthorizationService implements FeatureAuthorizationServiceInterface
{
private array $featureRoles = [
'admin-panel' => ['admin', 'super-admin'],
'beta-features' => ['beta-tester', 'admin'],
];

public function isAuthorized(string $featureName, ?array $context = null): bool
{
$user = $context['user'] ?? null;
if (!$user) {
return false;
}

$requiredRoles = $this->featureRoles[$featureName] ?? [];
$userRoles = $user['roles'] ?? [];

return !empty(array_intersect($requiredRoles, $userRoles));
}
}

// Use in FeatureManager
$authService = new RoleBasedAuthorizationService();
$featureManager = new FeatureManager(
$featureProvider,
$usageStatsProvider,
$featureProvider,
$authService
);

Experiment Tracking​

Automatically track which features are related to metrics:

use Toggly\FeatureManagement\Contracts\IFeatureExperimentProvider;

// FeatureProvider implements IFeatureExperimentProvider
$features = $featureProvider->getFeaturesForMetric('checkout-time');

// Returns array of feature names that use this metric
// Use this to automatically associate metrics with experiments

Batch Feature Evaluation​

Evaluate multiple features efficiently:

$features = ['feature-1', 'feature-2', 'feature-3'];
$results = [];

foreach ($features as $feature) {
$results[$feature] = $featureManager->isEnabled($feature);
}

// Or use array_map
$results = array_combine(
$features,
array_map(fn($f) => $featureManager->isEnabled($f), $features)
);

Feature Definitions​

Access raw feature definitions:

use Toggly\FeatureManagement\Core\FeatureProvider;

// Get all definitions
$definitions = $featureProvider->getAllFeatureDefinitions();

// Get specific definition
$definition = $featureProvider->getFeatureDefinition('my-feature');

if ($definition) {
echo "Feature: {$definition->key}\n";
echo "Enabled: " . ($definition->enabled ? 'Yes' : 'No') . "\n";
echo "Filters: " . count($definition->filters) . "\n";
}

Usage Statistics​

Manually send usage statistics:

use Toggly\FeatureManagement\Core\UsageStatsProvider;

// Stats are automatically collected when using FeatureManager
// Send them manually
$usageStatsProvider->sendStats();

// Get current stats (for debugging)
$stats = $usageStatsProvider->getStats(); // Internal method, may not be public

Custom Filters​

Create custom feature filters:

use Toggly\FeatureManagement\Models\FeatureFilter;

// In FeatureManager, you can extend filter evaluation
class CustomFeatureManager extends FeatureManager
{
protected function evaluateFilter(FeatureFilter $filter, ?array $context = null): bool
{
// Handle custom filter types
if ($filter->name === 'CustomFilter') {
return $this->evaluateCustomFilter($filter, $context);
}

// Fall back to parent implementation
return parent::evaluateFilter($filter, $context);
}

private function evaluateCustomFilter(FeatureFilter $filter, ?array $context): bool
{
$param = $filter->parameters['custom_param'] ?? null;
// Your custom logic
return $param === 'expected_value';
}
}

WebSocket Real-time Updates​

Use WebSocket for real-time feature updates in long-running PHP processes (CLI workers, Octane, Swoole). Traditional PHP-FPM skips the connection.

use Toggly\FeatureManagement\Http\WebSocketClient;

$websocketClient = new WebSocketClient($logger);

if ($websocketClient->connect($wsUrl, function (string $payload): void {
// Refresh feature definitions after a notification
})) {
echo "WebSocket connected\n";
} else {
echo "WebSocket not available, using polling\n";
}

if ($websocketClient->isRunning()) {
// WebSocket is active
}

$websocketClient->disconnect();

Error Handling​

Implement robust error handling:

try {
$enabled = $featureManager->isEnabled('my-feature');
} catch (\Toggly\FeatureManagement\Exceptions\TogglyException $e) {
// Handle Toggly-specific errors
logger()->error('Toggly error', ['exception' => $e]);
$enabled = false; // Fallback
} catch (\Exception $e) {
// Handle other errors
logger()->error('Unexpected error', ['exception' => $e]);
$enabled = false; // Fallback
}

// Always provide fallback behavior
if ($enabled) {
// Feature enabled
} else {
// Feature disabled or error occurred
}

Performance Optimization​

Optimize for performance:

// 1. Use snapshot providers for offline support
$snapshotProvider = new CacheSnapshotProvider($cache);
$featureProvider->setSnapshotProvider($snapshotProvider);

// 2. Configure appropriate refresh intervals
$settings = new TogglySettings([
'refresh_interval' => 600, // 10 minutes for stable features
]);

// 3. Batch feature evaluations
$features = ['f1', 'f2', 'f3'];
$results = array_map(fn($f) => $featureManager->isEnabled($f), $features);

// 4. Cache feature results in your application
$cacheKey = "feature:my-feature";
$enabled = cache()->remember($cacheKey, 60, function () use ($featureManager) {
return $featureManager->isEnabled('my-feature');
});

Testing​

Mock feature manager for testing:

use PHPUnit\Framework\TestCase;
use Toggly\FeatureManagement\Core\FeatureManager;

class FeatureManagerMock extends FeatureManager
{
private array $mockFeatures = [];

public function mockFeature(string $feature, bool $enabled): void
{
$this->mockFeatures[$feature] = $enabled;
}

public function isEnabled(string $featureName, ?array $context = null): bool
{
return $this->mockFeatures[$featureName] ?? false;
}
}

// In tests
$mockManager = new FeatureManagerMock();
$mockManager->mockFeature('test-feature', true);

$this->assertTrue($mockManager->isEnabled('test-feature'));

Best Practices​

  1. Use Context: Always provide user context for accurate targeting
  2. Handle Errors: Always provide fallback behavior
  3. Monitor State Changes: Use state service callbacks for important features
  4. Track Metrics: Record metrics for experiments
  5. Optimize Performance: Use caching and snapshot providers
  6. Test Thoroughly: Mock feature manager in tests
  7. Log Appropriately: Log errors but not every feature check