magmasoftwareengineering/rollout-group-slim

Slim 4 integration for magmasoftwareengineering/rollout — feature flag admin controllers, multi-storage replication, Twig helpers, and a Symfony Console command. Successor to the previous opensoft/rollout-based wrapper.

Maintainers

Package info

bitbucket.org/magmasoftwareengineering/rollout-group-slim

pkg:composer/magmasoftwareengineering/rollout-group-slim

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

3.2.1 2026-05-20 14:56 UTC

README

Version: 3.x | PHP: >=8.3 | License: MIT

Status

The original opensoft/rollout upstream has been unmaintained since 2017. This package now depends on magmasoftwareengineering/rollout — the maintained successor — rather than the abandoned opensoft/rollout: ^2.3 it wrapped previously. The engine package is at 3.0.0 and ships strict types, modernised storage adapters (including a rewritten MongoDBStorageAdapter for ext-mongodb and a new Psr16StorageAdapter), a JSON wire format with backwards-compat reader, and real interfaces.

The Slim integration here is unchanged in spirit: feature-flag admin controllers, multi-storage replication via GroupDefinitionAwareRollout, Twig helpers, a Symfony Console command, and the FeatureFlaggableTrait. Existing call sites that target rollout's public API (isActive, activateGroup, activatePercentage, etc.) continue to work without changes.

A parallel OpenFeature Provider is now available — see Forward direction below.

Overview

The Rollout Group Slim (magmasoftwareengineering/rollout-group-slim) provides feature flagging and user group management for Slim 4 applications. It builds on magmasoftwareengineering/rollout ^3.0 (the maintained successor to opensoft/rollout) to control feature availability, conduct A/B testing, and gradually roll features out to users or groups.

This library also builds upon Slim Base and Slim Module Middleware to provide enterprise-grade feature management capabilities.

Key Features

Feature Flagging

  • Feature toggles - Control feature visibility at runtime
  • Group-based activation - Target specific user groups for feature rollout
  • Percentage rollout - Gradually roll out features to a percentage of users
  • User-specific flags - Enable features for specific users or IDs
  • Admin interface - Built-in UI for managing feature flags

Group Management

  • Custom group definitions - Define custom user groups (e.g., "beta_users", "enterprise_customers")
  • Group-definition-aware Rollout - Extend base Rollout with group support
  • Closure-based groups - Flexible group definition using PHP closures
  • Persistent storage - Support for multiple storage backends (Redis, database, etc.)

Integration

  • Twig integration - Template helpers for conditional feature rendering
  • Trait-based API - FeatureFlaggableTrait for adding flagging to controllers/services
  • CLI management - Console commands for managing feature flags
  • CSV export/import - Bulk feature flag management

Dependencies

php: >=8.3
magmasoftwareengineering/slim-base: ^3.2              (HTTP controllers)
magmasoftwareengineering/slim-module-middleware: ^2.1 (Module system)
magmasoftwareengineering/rollout: ^3.0                (Feature flagging engine — successor to opensoft/rollout)
akrabat/ip-address-middleware: ^2.6                   (IP address detection)
kanellov/slim-twig-flash: ^0.2.0                      (Flash messages)
league/csv: ^9.28                                     (CSV handling)
slim/http: ^1.4                                       (HTTP utilities)
symfony/console: ^7.4 || ^8.0                         (CLI framework)

Installation

composer require magmasoftwareengineering/rollout-group-slim:^3.0

This automatically includes:

  • magmasoftwareengineering/slim-base:^3.0
  • magmasoftwareengineering/slim-module-middleware:^2.0
  • And all their dependencies

Usage Examples

Basic Feature Flagging

1. Define a Group

<?php
// src/modules/Features/GroupDefinition/BetaUsersGroup.php
namespace MyApp\Features\GroupDefinition;

use MagmaSoftwareEngineering\Rollout\Rollout\GroupDefinitionInterface;

class BetaUsersGroup implements GroupDefinitionInterface {
    public function getName(): string {
        return 'beta_users';
    }

    public function getCallback(): callable {
        return function($user) {
            // $user is typically a user ID or User entity
            // Return true if user is in the beta group
            return in_array($user, [1, 2, 3, 4, 5], true);
        };
    }
}

2. Add Feature to Container

<?php
// src/modules/Features/dependencies.php
use DI\Container;
use MyApp\Features\GroupDefinition\BetaUsersGroup;

return static function (Container $container): void {
    $container->set(BetaUsersGroup::class, new BetaUsersGroup());
};

3. Register Group with Rollout

<?php
// Initialize in your bootstrap or controller
/** @var GroupDefinitionAwareRollout $rollout */
$rollout->addGroupDefinition($container->get(BetaUsersGroup::class));

Using Feature Flags in Controllers

<?php
// src/modules/Features/Controller/FeatureController.php
namespace MyApp\Features\Controller;

use MagmaSoftwareEngineering\Slim\Controller\AbstractController;
use MagmaSoftwareEngineering\Rollout\Interfaces\FeatureFlaggableInterface;
use MagmaSoftwareEngineering\Rollout\Interfaces\RolloutInterface;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;

class FeatureController extends AbstractController implements FeatureFlaggableInterface {
    use FeatureFlaggableTrait;

    public function getFeatures(Request $request, Response $response): Response {
        $userId = $this->getCurrentUserId();

        $features = [];

        // Check if feature is active for this user
        if ($this->getFeatureFlagging()->isActive('new_dashboard', $userId)) {
            $features['new_dashboard'] = true;
        }

        // Check if feature is active for a group
        if ($this->getFeatureFlagging()->isActiveInGroup('beta_features', 'beta_users', $userId)) {
            $features['beta_features'] = true;
        }

        // Percentage-based rollout
        if ($this->getFeatureFlagging()->isActive('gradual_rollout', $userId, 10)) {
            // Only 10% of users see this feature
            $features['gradual_rollout'] = true;
        }

        return $this->jsonResponse($response, $features);
    }

    private function getCurrentUserId(): int {
        // Get current user ID from authentication service
        return $this->entityManager
            ->getRepository(User::class)
            ->findOneBy(['email' => $this->getAttribute('user_email')])
            ->getId();
    }
}

Using in Twig Templates

The library provides Twig extensions for easy feature flag checking:

{# templates/dashboard.twig #}

{% if rollout_is_active('new_dashboard', user.id) %}
    <div class="new-dashboard">
        {# New dashboard UI #}
        {{ include('components/new_dashboard.twig') }}
    </div>
{% else %}
    <div class="old-dashboard">
        {# Legacy dashboard UI #}
        {{ include('components/old_dashboard.twig') }}
    </div>
{% endif %}

{# Group-based flags #}
{% if rollout_is_active_in_group('beta_features', 'beta_users', user.id) %}
    <button class="beta-feature">Try Beta Features</button>
{% endif %}

Feature Management Commands

The library includes CLI commands for managing features:

# List all features
php console.php rollout:group:list

# Activate a feature for a user
php console.php rollout:group:activate --feature=new_dashboard --user=123

# Activate a feature for a group
php console.php rollout:group:activate --feature=beta_features --group=beta_users

# Deactivate a feature
php console.php rollout:group:deactivate --feature=new_dashboard --user=123

# Import features from CSV
php console.php rollout:group:import features.csv

# Export features to CSV
php console.php rollout:group:export features.csv

Advanced: Custom User Provider

Create a custom user provider to dynamically determine group membership:

<?php
// src/modules/Features/Service/CustomUserProvider.php
namespace MyApp\Features\Service;

use MagmaSoftwareEngineering\Rollout\Rollout\UserProviderInterface;
use Doctrine\ORM\EntityManagerInterface;

class CustomUserProvider implements UserProviderInterface {
    public function __construct(private EntityManagerInterface $em) {}

    public function isUserInGroup(string $userId, string $groupName): bool {
        $user = $this->em->find(User::class, $userId);
        if (!$user) {
            return false;
        }

        return match($groupName) {
            'beta_users' => $user->isBetaTester(),
            'enterprise' => $user->getAccountType() === 'enterprise',
            'admin' => $user->hasRole('ADMIN'),
            default => false,
        };
    }
}

Feature Flag Entity

Features are stored using RolloutFeature entity:

<?php
// Example: creating a feature programmatically
use MagmaSoftwareEngineering\Rollout\Entity\RolloutFeature;

$feature = new RolloutFeature();
$feature->setName('new_dashboard');
$feature->setDescription('New dashboard interface');
$feature->setPercentage(50);  // Rollout to 50% of users
$feature->setActive(true);

$this->entityManager->persist($feature);
$this->entityManager->flush();

Complete Application Example

Directory Structure

my-api/
├── src/
│   └── modules/
│       ├── Features/
│       │   ├── src/
│       │   │   ├── Controller/
│       │   │   │   ├── FeatureController.php
│       │   │   │   └── AdminController.php
│       │   │   ├── Service/
│       │   │   │   └── FeatureService.php
│       │   │   ├── GroupDefinition/
│       │   │   │   ├── BetaUsersGroup.php
│       │   │   │   ├── EnterpriseGroup.php
│       │   │   │   └── AdminGroup.php
│       │   │   └── dependencies.php
│       │   ├── Module.php
│       │   ├── routes.php
│       │   └── settings.php
│       └── User/
│           └── ...
├── public/
│   └── index.php
└── config/
    └── bootstrap.php

Module Routes

<?php
// src/modules/Features/routes.php
use Slim\Routing\RouteCollectorProxy;
use MyApp\Features\Controller\FeatureController;
use MyApp\Features\Controller\AdminController;

return static function (RouteCollectorProxy $app): void {
    // Public feature flag endpoints
    $app->group('/features', function (RouteCollectorProxy $group) {
        $group->get('', [FeatureController::class, 'getFeatures']);
        $group->post('', [FeatureController::class, 'create']);
    });

    // Admin feature management
    $app->group('/admin/features', function (RouteCollectorProxy $group) {
        $group->get('', [AdminController::class, 'list']);
        $group->get('/{id}', [AdminController::class, 'show']);
        $group->put('/{id}', [AdminController::class, 'update']);
        $group->delete('/{id}', [AdminController::class, 'delete']);
        $group->post('/import', [AdminController::class, 'import']);
        $group->get('/export', [AdminController::class, 'export']);
    });
};

Integration with Other Libraries

LibraryPurposeIntegration
Slim BaseHTTP controllers & settingsExtend AbstractController & implement FeatureFlaggableInterface
Slim Module MiddlewareModule systemLoaded as a module with routes & controllers
Doctrine BaseDatabase entities & repositoriesUses RolloutFeature entity
PHP DebugBar MiddlewareDeveloper debuggingFeature flags visible in debug bar

Forward direction — OpenFeature

A separate package, magmasoftwareengineering/rollout-openfeature-provider, bridges the rollout engine to the OpenFeature evaluation API. Slim applications can use both surfaces side-by-side:

  • This package continues to provide the admin controllers, Twig helpers, console command, and FeatureFlaggableTrait for rollout-shaped management of flags.
  • An OpenFeature Client, configured with the RolloutProvider, gives application code a vendor-neutral evaluation surface ($client->getBooleanValue, $client->getStringValue, etc.) backed by the same Rollout instance.
use MagmaSoftwareEngineering\Rollout\OpenFeature\RolloutProvider;
use OpenFeature\OpenFeatureAPI;

$api = OpenFeatureAPI::getInstance();
$api->setProvider(new RolloutProvider($rollout));   // same $rollout this package wires up

$client = $api->getClient('my-slim-app');
$client->getBooleanValue('chat', false);

The provider supports boolean flags as a direct delegate to Rollout::isActive, and typed flags (string / integer / float / object) via a data['value'] convention on Feature::getData(). The closure-based group definitions this wrapper provides via defineGroup() don't currently map onto OpenFeature's declarative targeting model — see docs/06-openfeature-native-replacement.md for the long-term path being explored.

Architecture

Rollout Group Slim Library
├── Feature Flagging
│   ├── GroupDefinitionAwareRollout (extends magmasoftwareengineering/rollout)
│   ├── RolloutFeature Entity
│   └── CLI Commands
├── Group Management
│   ├── GroupDefinitionInterface
│   ├── Custom group definitions
│   └── Closure-based rules
├── Twig Integration
│   ├── RolloutIsActiveExtension
│   └── RolloutIsActiveInGroupExtension
└── Controllers
    ├── Feature management UI
    └── Admin dashboard

Use Cases

1. Gradual Rollout

// Gradually rollout new feature to 10% → 25% → 50% → 100%
$rollout->isActive('new_feature', $userId, 10);  // Week 1
$rollout->isActive('new_feature', $userId, 25);  // Week 2
$rollout->isActive('new_feature', $userId, 50);  // Week 3
$rollout->isActive('new_feature', $userId, 100); // Week 4

2. Beta Testing

// Enable features only for beta testers
$rollout->isActiveInGroup('beta_feature', 'beta_users', $userId);

3. A/B Testing

// Test variant A vs variant B
if ($rollout->isActive('feature_variant_a', $userId, 50)) {
    // Show variant A to 50% of users
} else {
    // Show variant B to other 50%
}

4. Internal/Admin Features

// Feature only visible to admins
$rollout->isActiveInGroup('admin_panel', 'admins', $userId);

Related Libraries

Support & Contributing

For issues or contributions, please contact the maintainer:

  • Jeremy Coates - hello@phpcodemonkey.me.uk

License

MIT License - See LICENSE file for details