Aller au contenu

Installation

phil/feature-flags-bundle

Cette page mène une application nue jusqu'à un bundle fonctionnel : les prérequis, Composer, la classe du bundle, les tables de la base de données, les routes d'administration et le pare-feu devant elles. La liste complète des options se trouve dans configuration.md.

Prérequis

PrérequisVersionPourquoi
PHP8.5 ou plus récentDéclaré >=8.5 ; le code utilise les constantes de classe typées, les classes readonly et #[Override].
ext-mbstringtouteLes raisons, les acteurs, les descriptions et les responsables sont vérifiés en UTF-8 et mesurés en caractères.
ext-hashfournie avec PHPLes compartiments stables des déploiements progressifs et des expériences utilisent l'algorithme murmur3a de hash(). Elle est toujours compilée depuis PHP 7.4 ; elle n'est donc pas déclarée.
doctrine/dbal^4.5Le magasin en base de données et son schéma.
doctrine/orm^3.7L'écouteur de schéma se branche sur l'événement postGenerateSchema de l'ORM.
phil/circuit-breaker, phil/circuit-breaker-bundle^0.1@devLe port CircuitOverride qu'implémentent les indicateurs d'ouverture forcée, et les circuits dont les clés déclarent ces indicateurs.
phil/ddd-bundle^0.1@devLes exceptions InvalidValue et DomainError du domaine.
phil/enums^0.3@devAsValuableEnumTrait sur FlagKind et TargetingAttribute, dont values() écrit les messages d'erreur et les genres du formulaire d'administration.
psr/clock^1.0Chaque horodatage (lignes d'audit, expiration) vient d'une horloge PSR-20.
symfony/config, console, dependency-injection, http-foundation, http-kernel, routing^8.2L'arbre de configuration, la commande de liste, le câblage, le contrôleur d'administration, l'attribut de contrôleur, la condition de route.
twig/twig^3.21Les gabarits d'administration et les fonctions #[AsTwigFunction].

Doctrine DBAL 4.5. FlagTables construit ses tables avec Schema::edit() et les éditeurs de table, de colonne et de clé primaire, et FlagSchemaListener rend le schéma avec GenerateSchemaEventArgs::setSchema(), qui lance une exception à moins que Schema::edit() existe (DBAL 4.5 et plus récent). C'est pourquoi le bundle exige ^4.5.

Le bundle enregistre ses services avec des étiquettes sur lesquelles agissent d'autres bundles ; en pratique, l'application a donc aussi :

BundleNécessaire pour
symfony/framework-bundleLe service clock par défaut (avec symfony/clock), le service router qu'utilise l'administration, les étiquettes kernel.reset, console.command, kernel.event_listener et routing.condition_service.
symfony/twig-bundleLe service twig de l'administration, et les étiquettes twig.attribute_extension / twig.runtime des fonctions Twig.
doctrine/doctrine-bundleSeulement avec le magasin en base de données : le service doctrine.dbal.<name>_connection et l'étiquette doctrine.event_listener.
symfony/expression-languageSeulement pour les conditions de route (condition: "service('feature_flags')…").

Bases de données : le magasin en base de données est testé sur SQLite, MySQL 8 et PostgreSQL.

Installer avec Composer

composer require phil/feature-flags-bundle

Le bundle exige phil/ddd-bundle, phil/circuit-breaker et phil/circuit-breaker-bundle à une stabilité de développement (^0.1@dev). Tant qu'ils ne sont pas publiés, l'application déclare pour eux un dépôt path ou vcs et accepte @dev pour ces paquets.

Enregistrer le bundle

Avec Symfony Flex, le bundle est ajouté pour vous à config/bundles.php ; sinon, ajoutez-le à la main, après les bundles dont il dépend :

// config/bundles.php
return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],   // database store only
    Phil\CircuitBreakerBundle\PhilCircuitBreakerBundle::class => ['all' => true],   // circuits forced open by a flag
    Phil\FeatureFlagsBundle\PhilFeatureFlagsBundle::class => ['all' => true],
];

Sans PhilCircuitBreakerBundle, aucun circuit n'est déclaré, ni aucun indicateur d'ouverture forcée ; les indicateurs fonctionnent de la même façon.

La plus petite configuration

La clé de configuration est phil_feature_flags. Sans aucun adaptateur, avec des indicateurs déclarés en YAML et un contexte vide :

# config/packages/phil_feature_flags.yaml
phil_feature_flags:
    context_resolver: phil_feature_flags.context_resolver.anonymous
    flags:
        new-checkout: true

Sans context_resolver, le bundle attend un service nommé d'après l'interface Phil\FeatureFlagsBundle\Application\Flag\EvaluationContextResolver : implémentez-la dans src/ (avec l'autowiring, une implémentation unique reçoit l'alias de l'interface) ; voyez extension-points.md.

Le magasin en base de données et sa migration

Activez le magasin pour conserver dans la base de données les états actifs et les indicateurs créés dans l'administration :

phil_feature_flags:
    doctrine:
        connection: default     # a DBAL connection name of DoctrineBundle

Le magasin possède deux tables, phil_feature_flags_definition et phil_feature_flags_state (database-store.md décrit leurs colonnes). Le bundle ne fournit pas de migration : FlagSchemaListener ajoute les deux tables au schéma que l'ORM génère pour cette connexion, et les outils habituels les voient donc :

bin/console doctrine:migrations:diff        # writes the migration that creates the two tables
bin/console doctrine:migrations:migrate
bin/console doctrine:schema:validate        # does not ask to drop them

L'écouteur ne s'exécute que lorsque l'outil de schéma de l'ORM s'exécute pour un gestionnaire d'entités sur cette même connexion. Une application sans gestionnaire d'entités sur cette connexion crée les tables à partir de la même définition :

use Doctrine\DBAL\Schema\Schema;
use Phil\FeatureFlagsBundle\Infrastructure\Doctrine\FlagTables;

foreach (FlagTables::configure(new Schema())->toSql($connection->getDatabasePlatform()) as $sql) {
    $connection->executeStatement($sql);
}

La bascule

Changer un indicateur à l'exécution exige la bascule auditée, qui exige un magasin d'états (le magasin en base de données en fournit un) et deux adaptateurs de l'application, un autorisateur et un auditeur :

phil_feature_flags:
    doctrine: true
    toggle:
        authorizer: App\Flags\PermissionAuthorizer
        auditor: App\Flags\AuditTrailAuditor

Voyez toggles-and-edits.md et extension-points.md.

Les routes d'administration

Activez les écrans (ils exigent la bascule) et nommez le service qui dit qui est l'opérateur :

phil_feature_flags:
    admin:
        actor_provider: App\Flags\OperatorProvider

Importez ensuite les routes du bundle sous un préfixe de votre choix :

# config/routes/phil_feature_flags.yaml
phil_feature_flags_admin:
    resource: '@PhilFeatureFlagsBundle/config/routes/admin.php'
    prefix: /admin/flags

Les cinq routes qu'il déclare sont listées dans admin.md.

Le pare-feu devant l'administration

Le bundle n'authentifie personne. Placez le préfixe derrière le pare-feu de l'application et exigez un rôle :

# config/packages/security.yaml
security:
    access_control:
        - { path: ^/admin/flags, roles: ROLE_FLAG_OPERATOR }

À l'intérieur, l'ActorProvider nomme l'opérateur (un écran répond 403 lorsqu'il retourne null), l'autorisateur décide de chaque changement, et chaque envoi de formulaire est vérifié comme provenant de la même origine. Les détails sont dans admin.md.

Vérifier l'installation

bin/console debug:config phil_feature_flags
bin/console phil:feature-flags:list

La liste montre chaque indicateur que déclarent la configuration et la base de données, avec son état et sa source (console.md).