laravel/pennant
Laravel Pennant is a simple, lightweight feature flag library for Laravel. Manage, evaluate, and roll out features safely across environments with an easy API and first-party integration, backed by official Laravel documentation.
Installation:
composer require laravel/pennant
php artisan pennant:install
This publishes the migration and config file.
Run Migration:
php artisan migrate
Define a Feature Flag:
use Laravel\Pennant\Feature;
Feature::create('new-dashboard', 'boolean', true);
First Usage:
if (Feature::get('new-dashboard')) {
// Show new dashboard
}
config/pennant.php (drivers, cache settings, default scope).Feature facade for quick access.EnsureFeatureIsActive for route/endpoint protection.@feature and @featureany for view-level checks.Feature Flag Management:
// Create a feature
Feature::create('beta-invitations', 'boolean', false);
// Toggle a feature
Feature::set('beta-invitations', true);
// Bulk updates
Feature::setMany([
'feature-a' => true,
'feature-b' => false,
]);
Scoped Features (e.g., per user/tenant):
// Set scope (e.g., user ID)
Feature::scope('user', auth()->id());
// Check scoped feature
if (Feature::get('premium-features')) {
// Show premium content
}
Middleware Integration:
Route::get('/admin', function () {
// ...
})->middleware('feature:admin-panel');
Or globally for all routes:
Feature::middleware(['admin-panel', 'beta-features']);
Blade Directives:
@feature('new-ui')
<div>New UI Content</div>
@endif
@featureany(['experimental-a', 'experimental-b'])
<div>Experimental Features</div>
@endfeatureany
Events & Hooks:
// Listen for feature updates
Feature::updated(function ($feature) {
Log::info("Feature {$feature->name} updated to {$feature->value}");
});
// Use before hooks (e.g., for validation)
Feature::before('payment-gateway', function () {
return auth()->user()->hasRole('admin');
});
Feature::flushCache() after bulk updates or config changes.Feature facade or use Feature::fake() for isolated tests.database, cache, and custom drivers. Configure in config/pennant.php.Feature facade with custom methods:
Feature::macro('isEnabledForUser', function ($user, $featureName) {
return Feature::scope('user', $user->id)->get($featureName);
});
Scope Leaks:
Feature::scope('user', $userId);
// ... logic ...
Feature::scope(null); // Reset scope
Feature::defaultScope() to avoid hardcoding scopes.Cache Invalidation:
Feature::flushCache().cache_flush_after_write in config/pennant.php to auto-flush on writes.Type Safety:
Feature::create('user-role', UserRole::class)) require proper serialization.Feature::resolve() to get typed instances:
$role = Feature::resolve('user-role');
Middleware Conflicts:
EnsureFeatureIsActive middleware throws a 403 by default. Customize the response in your middleware:
public function handle($request, Closure $next, $feature)
{
if (!Feature::get($feature)) {
abort(404); // Or redirect()
}
return $next($request);
}
Bulk Operations:
Feature::setMany() in loops. Batch updates for performance:
Feature::setMany($features); // Single DB query
if (Feature::has('non-existent-feature')) {
// ...
}
dd(Feature::scopes()); // Shows current scopes
Feature::updated(function ($feature) {
Log::debug("Feature {$feature->name} set to {$feature->value}");
});
Custom Drivers:
Laravel\Pennant\Contracts\Driver for non-DB storage (e.g., Redis, S3).config/pennant.php:
'driver' => \App\Pennant\Drivers\CustomDriver::class,
Decorators:
Laravel\Pennant\Decorators\Decorator to add logic (e.g., A/B testing):
Feature::decorate(function ($feature) {
if ($feature->name === 'ab-test') {
return Feature::get('ab-test') === 'variant-b';
}
return $feature->value;
});
Events:
FeatureUpdated, FeaturesPurged, or FeatureCreated events for analytics/auditing.Blade Extensions:
Blade::directive('feature', function ($expression) {
return "<?php if (\\Laravel\\Pennant\\Feature::get({$expression})): ?>";
});
loadAll() to preload features for a scope (reduces N+1 queries).cache_flush_after_write if using a fast cache (e.g., Redis) and manually flush when needed.unique constraint improvements in v1.8.0 to avoid deadlocks.How can I help you explore Laravel packages today?