diff --git a/MILESTONES.md b/MILESTONES.md new file mode 100644 index 0000000..47bfaae --- /dev/null +++ b/MILESTONES.md @@ -0,0 +1,59 @@ +# FlagPole Milestones + +This document outlines the roadmap for enhancing FlagPole with advanced targeting, persistence, and observability features. + +## Table of Contents +- [Milestone 1: Advanced Rule Engine](#milestone-1-advanced-rule-engine) +- [Milestone 2: Pluggable Targeting Keys](#milestone-2-pluggable-targeting-keys) +- [Milestone 3: Persistent Repositories](#milestone-3-persistent-repositories) +- [Milestone 4: Observability & Logging](#milestone-4-observability--logging) +- [Milestone 5: Documentation & Polish](#milestone-5-documentation--polish) +- [Milestone 6: Refactoring & DRY](#milestone-6-refactoring--dry) +- [Milestone 7: Validation & Safety](#milestone-7-validation--safety) + +## Milestone 1: Advanced Rule Engine +Move beyond simple allow-lists to support complex attribute-based targeting. + +- [x] Define `Rule` DTO/Interface (`src/Rule.php`). +- [x] Add `rules` collection to `Flag` DTO. +- [x] Implement rule evaluation logic in `Evaluator` (supporting operators like `eq`, `gt`, `lt`, `in`, `contains`). +- [x] Update `Evaluator::evaluate` precedence: `allowList` > `enabled` > `rules` > `rolloutPercentage`. + +## Milestone 2: Pluggable Targeting Keys +Allow explicit control over which context attribute is used for rollout hashing. + +- [x] Add optional `targetingKey` property to `Flag`. +- [x] Refactor `Evaluator::resolveTargetingKey` to honor `Flag->targetingKey` if present. +- [x] Modernize hashing implementation in `Evaluator::computeBucket` for PHP 8.2+. + +## Milestone 3: Persistent Repositories +Provide out-of-the-box support for non-volatile flag storage. + +- [x] Implement `JsonFileRepository` in `src/Repository/JsonFileRepository.php`. +- [x] Ensure robust JSON parsing and mapping to `Flag` objects. + +## Milestone 4: Observability & Logging +Provide insights into why flags are being enabled or disabled. + +- [x] Integrate PSR-3 `LoggerInterface` into `FeatureManager` and `Evaluator`. +- [x] Implement detailed logging for evaluation outcomes (e.g., which rule or strategy matched). +- [x] (Optional) Create `EvaluationResult` DTO for programmatic access to evaluation reasons. + +## Milestone 5: Documentation & Polish +- [x] Update `README.md` with examples for new features. +- [x] Add comprehensive tests for Rule Engine and JSON Repository. +- [x] Verify zero regression for existing simple flag usage. + +## Milestone 6: Refactoring & DRY +Centralize logic and remove duplication to improve maintainability. + +- [x] Extract flag hydration logic into a dedicated `FlagHydrator` class to be reused across repositories. +- [x] Refactor `InMemoryFlagRepository` to use the new hydration logic. +- [x] Refactor `JsonFileRepository` to use the new hydration logic. + +## Milestone 7: Validation & Safety +Enhance the engine to be more robust and developer-friendly. + +- [x] Add validation to the rule engine to handle or log unknown operators instead of failing silently. +- [x] Implement configuration validation to ensure `Flag` and `Rule` objects are correctly formed before evaluation. +- [x] Optimize `JsonFileRepository` to avoid unnecessary parsing or consider lazy-loading if the config grows large. diff --git a/SPECS.md b/SPECS.md new file mode 100644 index 0000000..bd0f823 --- /dev/null +++ b/SPECS.md @@ -0,0 +1,54 @@ +# FlagPole Specifications + +This document describes the current technical specifications and architecture of the FlagPole feature flagging library. + +## Core Concepts + +### Flag +An immutable data object representing a feature flag and its evaluation strategies. +- **Properties**: + - `name` (string): Unique identifier. + - `enabled` (bool|null): Explicit override. If `true`/`false`, evaluation stops here. + - `rolloutPercentage` (int|null): 0-100 value for gradual rollout. + - `allowList` (list): Keys that always receive `true`. + - `rules` (list): Complex attribute-based targeting rules. + - `targetingKey` (string|null): Optional override for the attribute used in rollout/allowList. + +### Context +A container for attributes (e.g., `userId`, `email`, `userGroup`) used during evaluation. +- **Targeting Keys**: By default, the evaluator looks for `key`, `userId`, `id`, or `email` (in that order), unless overridden by `Flag->targetingKey`. + +### Evaluator +The engine that applies flag strategies against a context. +- **Precedence**: + 1. `allowList`: If the targeting key is in the list, return `true`. + 2. `enabled`: If non-null, return its boolean value. + 3. `rules`: If any rule matches the context attributes, return `true`. + 4. `rolloutPercentage`: Hash-based stable bucketing (0-99). + 5. `fallback`: Returns the user-provided default. + +### FeatureManager +The main entry point for the consumer. +- **Method**: `isEnabled(string $flagName, ?Context $context = null, bool $default = false): bool` +- **Logging**: Supports PSR-3 logging of evaluation reasons. + +## Technical Details + +### Rollout Hashing +- Algorithm: `xxh3(flagName + ":" + targetingKey)`. +- Normalization: `hexdec(substr(hash, 0, 8)) % 100`. +- Bucketing: 0-99. + +### Repositories +- `FlagRepositoryInterface`: Defines `get(string $name)` and `all()`. +- `InMemoryFlagRepository`: Provided for testing and simple setups. +- `JsonFileRepository`: Loads flags from a JSON file. Now supports lazy-loading hydration for better performance. + +### FlagHydrator +A dedicated component responsible for transforming raw configuration arrays into validated `Flag` and `Rule` objects. +- **Validation**: Ensures all rules have required fields and valid operators (`eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`, `contains`). +- **Reuse**: Used by both `InMemoryFlagRepository` and `JsonFileRepository` to ensure consistent flag creation. + +## Constraints +- PHP 8.2+ required. +- No external dependencies (PSR interfaces excepted in future).