Toggly Feature Management for PHP
September 6, 2026 · View on GitHub
Official PHP SDK for Toggly feature flags — Composer packages for core, Laravel, and WordPress.
Repository layout
| Path | Package name | Packagist publish surface |
|---|---|---|
Repo root (src/, root composer.json) | toggly/feature-management-php | This repo (Packagist GitHub URL) |
packages/laravel/ | toggly/laravel | Mirror ops-ai/Toggly.FeatureManagement.PHP.Laravel |
packages/wordpress/ | toggly/wordpress | Mirror ops-ai/Toggly.FeatureManagement.PHP.Wordpress |
Root composer.json is the publishable core package. Path repositories under
packages/* exist for local CI so Laravel/WordPress can depend on core in-tree.
Namespaces: Toggly\FeatureManagement\, Toggly\Laravel\, Toggly\WordPress\.
Installation
Core only
composer require toggly/feature-management-php
Laravel (includes core)
composer require toggly/laravel
WordPress (includes core)
composer require toggly/wordpress
toggly/laravel and toggly/wordpress are published on Packagist from the
mirror repos (ops-ai/Toggly.FeatureManagement.PHP.Laravel and
ops-ai/Toggly.FeatureManagement.PHP.Wordpress). For local monorepo development,
path repositories under packages/* are configured in the root composer.json.
For non-Composer WordPress installs, copy or symlink the toggly/wordpress
package directory to wp-content/plugins/toggly/ so WordPress loads toggly.php.
Migration from pre-1.0: if you previously relied on Laravel/WordPress classes
from toggly/feature-management-php alone, also require toggly/laravel or
toggly/wordpress. Core 1.0 no longer ships those namespaces.
Features
- Full Feature Parity: Matches the functionality of the .NET Toggly.FeatureManagement library
- Signed Definitions: ECDSA signature verification for secure feature definitions
- Real-time Updates: WebSocket support for instant feature updates (with polling fallback)
- Usage Statistics: Automatic tracking of feature usage and user analytics
- Metrics Collection: Support for measurements, observations, and counters
- Snapshot Providers: Cache, database, and file-based snapshot storage
- Laravel Integration: Native Laravel service provider, facade, and middleware
- WordPress Plugin: Full WordPress plugin with admin interface and hooks
- PSR Standards: Built on PSR-4, PSR-11, PSR-16, PSR-18, and PSR-17
Quick Start
Laravel
Package auto-discovery registers the service provider and Toggly facade.
Manual config/app.php registration is only needed if auto-discovery is disabled.
- Publish the configuration:
php artisan vendor:publish --tag=toggly-config
- Configure in
.env:
TOGGLY_APP_KEY=your-app-key
TOGGLY_ENVIRONMENT=Production
TOGGLY_USE_SIGNED_DEFINITIONS=false
- Use in your code:
use Toggly\Laravel\Facades\Toggly;
use Toggly\FeatureManagement\Contracts\FeatureStateServiceInterface;
use Toggly\FeatureManagement\Core\MetricsService;
// Check if feature is enabled
if (Toggly::isEnabled('new-checkout')) {
return view('checkout.v2');
}
// With evaluation context (identity / groups / claims / request preferred;
// legacy userId is still accepted)
$enabled = Toggly::isEnabled('premium-feature', [
'identity' => (string) $user->id,
'groups' => $user->groups ?? [],
]);
// State change handlers live on FeatureStateService (not the facade)
app(FeatureStateServiceInterface::class)->whenFeatureTurnsOn('new-api', function () {
// Initialize new API
});
// Metrics live on MetricsService
$metrics = app(MetricsService::class);
$metrics->measure('checkout-completed', 125.50);
$metrics->observe('active-users', 1500);
$metrics->incrementCounter('api-calls', 1);
- Use middleware in routes (alias
featureif not already registered):
Route::get('/new-feature', function () {
return view('new-feature');
})->middleware('feature:new-feature');
WordPress
-
Install the plugin by copying to
wp-content/plugins/toggly/ -
Activate the plugin in WordPress admin
-
Configure in Settings > Toggly:
- App Key
- Environment
- Base URL (optional)
- Use Signed Definitions (optional)
-
Use in templates:
<?php if (toggly_is_enabled('new-header')): ?>
<?php get_template_part('header', 'new'); ?>
<?php endif; ?>
- Use shortcode:
[toggly_feature name="premium-content"]
<!-- Premium content here -->
[/toggly_feature]
- Use hooks in
functions.php:
add_action('toggly_feature_turns_on', function($featureKey) {
if ($featureKey === 'new-theme') {
// Activate new theme
}
});
Core Library Usage
Basic Usage
use Toggly\FeatureManagement\Config\TogglySettings;
use Toggly\FeatureManagement\Core\FeatureProvider;
use Toggly\FeatureManagement\Core\FeatureManager;
use Toggly\FeatureManagement\Http\TogglyHttpClient;
$settings = new TogglySettings([
'app_key' => 'your-app-key',
'environment' => 'Production',
]);
$httpClient = new TogglyHttpClient(/* PSR-18 client */, /* PSR-17 factory */, $settings->getBaseUrl());
$featureProvider = new FeatureProvider($settings, $httpClient, /* state service */);
$featureManager = new FeatureManager($featureProvider, /* usage stats */, /* secure provider */);
// Check feature
if ($featureManager->isEnabled('my-feature')) {
// Feature is enabled
}
Snapshot Providers
Cache Provider (PSR-16)
use Toggly\FeatureManagement\Storage\SnapshotProviders\CacheSnapshotProvider;
use Toggly\FeatureManagement\Storage\SnapshotSettings;
$snapshotProvider = new CacheSnapshotProvider(
$cache, // PSR-16 cache implementation
new SnapshotSettings(['document_name' => 'toggly_features']),
86400 // TTL in seconds
);
Database Provider (PDO)
use Toggly\FeatureManagement\Storage\SnapshotProviders\DatabaseSnapshotProvider;
$snapshotProvider = new DatabaseSnapshotProvider(
$pdo, // PDO instance
new SnapshotSettings(['document_name' => 'toggly_features'])
);
File Provider
use Toggly\FeatureManagement\Storage\SnapshotProviders\FileSnapshotProvider;
$snapshotProvider = new FileSnapshotProvider(
'/path/to/snapshots',
new SnapshotSettings(['document_name' => 'toggly_features.json'])
);
Configuration
TogglySettings
$settings = new TogglySettings([
'app_key' => 'your-app-key',
'environment' => 'Production',
'base_url' => 'https://app.toggly.io/',
'use_signed_definitions' => true,
'allowed_key_ids' => ['key-id-1', 'key-id-2'],
'refresh_interval' => 300, // 5 minutes
'app_version' => '1.0.0',
'instance_name' => 'server-1',
'undefined_enabled_on_development' => false,
]);
Advanced Features
Feature State Change Handlers
$stateService = $container->get(FeatureStateServiceInterface::class);
// Register callback
$id = $stateService->whenFeatureTurnsOn('new-feature', function() {
// Initialize feature
});
// Unregister
$stateService->unregisterFeatureStateChange('new-feature', $id);
Custom Metrics
$metricsService = $container->get(MetricsServiceInterface::class);
// Record measurement (aggregated over time)
$metricsService->measure('revenue', 1250.50);
// Record observation (point-in-time)
$metricsService->observe('active-users', 1500);
// Increment counter
$metricsService->incrementCounter('api-calls', 1);
Usage + metrics telemetry (gRPC parity)
UsageStatsProvider and MetricsService batch in-process and flush via
sendStats() / sendMetrics() (or flush()). Timers are host-driven:
register Laravel schedule / WP-Cron (see Laravel README). A best-effort
shutdown flush is registered automatically.
Transport (prefer gRPC):
- When
ext-grpcand Composer packagegoogle/protobufare available, the SDK dials native gRPCUsage.SendStats/Metrics.SendMetricsagainst the metrics base URL (defaulthttps://app.toggly.io/). - Otherwise it soft-fails and posts the same wire shape over HTTPS JSON to
api/usage/statsandapi/metrics(gateway-accepted path). Feature evaluation is never blocked by telemetry failures.
gRPC metadata: the SDK user-agent is sent as metadata key ua. PHP's
gRPC extension lowercases HTTP/2 metadata keys; this matches .NET/Go/Node
UA on the wire (case-insensitive).
# Optional native gRPC
pecl install grpc
composer require google/protobuf
$usage = $container->get(UsageStatsProvider::class);
$usage->recordUsage('checkout');
$usage->recordView('checkout'); // viewed / rendered
$usage->sendStats();
Wire payloads use multi-variant maps (variantStats / variantValues) and
UTF-8 FNV-1a signed int32 identity hashes (Go/Node/Python parity).
Custom Context Provider
class MyContextProvider implements FeatureContextProviderInterface
{
public function getContextIdentifier(): ?string
{
// Return unique user identifier
return $this->getCurrentUserId();
}
// ... implement other methods
}
Evaluation context (filter parity)
Pass an EvalContext-shaped array to FeatureManager::isEnabled /
evaluateDefinition:
$enabled = $featureManager->isEnabled('my-feature', [
'identity' => (string) $user->id,
'groups' => ['beta'],
'claims' => ['role' => 'admin'],
'request' => [
'userAgent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
'acceptLanguage' => $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? null,
'country' => null,
],
]);
// Or map headers (country: cf-ipcountry → x-vercel-ip-country → cloudfront-viewer-country)
use Toggly\FeatureManagement\Core\HttpRequestMapper;
$context = HttpRequestMapper::mergeIntoContext($headers, [
'identity' => 'u',
'groups' => ['beta'],
]);
Segment filters (BrowserFamily, BrowserLanguage, Country, DeviceType,
OS / OperatingSystem) and UserClaims are evaluated in core
FeatureManager. Classes under packages/laravel/src/Filters/ are legacy
helpers and are not the evaluation path.
Requirements
- PHP 7.4 or higher (8.2–8.4 recommended; CI covers 8.2, 8.3, and 8.4)
- PSR-18 HTTP client (e.g., Guzzle, Symfony HTTP Client)
- PSR-16 cache (optional, for snapshot provider)
- PSR-11 container (optional, for dependency injection)
Laravel Requirements
- Laravel 8.0 or higher
illuminate/supportilluminate/http
WordPress Requirements
- WordPress 5.0 or higher
- No external dependencies (uses WordPress APIs)
Architecture
The library follows a modular architecture:
- Core Library: Framework-agnostic core functionality
- Laravel Integration: Service provider, facade, and middleware (Laravel
Filters/classes are legacy; coreFeatureManagerowns filter evaluation) - WordPress Plugin: Full plugin with admin interface
Core Components
FeatureProvider: Fetches and manages feature definitionsFeatureManager: Evaluates features (including filter-parity segment / UserClaims filters) with stats trackingHttpRequestMapper: Maps HTTP headers into EvalContextrequestFeatureStateService: Manages state change notificationsUsageStatsProvider: Collects and sends usage statisticsMetricsService: Collects custom metrics for experimentsEcdsaSignatureVerifier: Verifies signed definitionsJwkManager: Manages JSON Web Keys for signature verification
Snapshot Providers
Three snapshot provider implementations are available:
- CacheSnapshotProvider: Uses PSR-16 cache (Redis, Memcached, etc.)
- DatabaseSnapshotProvider: Uses PDO (MySQL, PostgreSQL, SQLite)
- FileSnapshotProvider: Uses file system storage
Development
Running Tests
composer test
Code Style
The project follows PSR-12 coding standards.
Contributing
Please open an issue first for bugs and feature ideas, then follow CONTRIBUTING.md. Large PRs without prior discussion may be closed.
Security
Report vulnerabilities privately via GitHub Private Vulnerability Reporting. See SECURITY.md. Do not file public issues for security reports.
License
Support
- Guides and API references: docs.toggly.io
- Bugs and features: GitHub Issues (structured templates)
- Publishing:
PUBLISHING.md - Code of conduct:
CODE_OF_CONDUCT.md