PHPackages                             rasuvaeff/yii3-feature-flags - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. rasuvaeff/yii3-feature-flags

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

rasuvaeff/yii3-feature-flags
============================

Feature flags, kill switches and percentage rollout for Yii3 applications

v1.0.2(3w ago)0277↓88.1%3BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jun 14Pushed 1w agoCompare

[ Source](https://github.com/rasuvaeff/yii3-feature-flags)[ Packagist](https://packagist.org/packages/rasuvaeff/yii3-feature-flags)[ Docs](https://github.com/rasuvaeff/yii3-feature-flags)[ RSS](/packages/rasuvaeff-yii3-feature-flags/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (17)Versions (4)Used By (3)

rasuvaeff/yii3-feature-flags
============================

[](#rasuvaeffyii3-feature-flags)

[![Stable Version](https://camo.githubusercontent.com/62fbd140fa2174cde8cb27f1b9f48fdd915f5d3824ea07a94c17812df1fe3330/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7261737576616566662f796969332d666561747572652d666c6167732e7376673f6c6162656c3d737461626c65)](https://packagist.org/packages/rasuvaeff/yii3-feature-flags)[![Total Downloads](https://camo.githubusercontent.com/4a73b30d2ae5107563d382a97f2e365c16e893a5321efdb66400d984fd66b567/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7261737576616566662f796969332d666561747572652d666c6167732e737667)](https://packagist.org/packages/rasuvaeff/yii3-feature-flags)[![Build](https://camo.githubusercontent.com/d20548882c777307de771fedf38e18415698a5b92dc440d41c56d90236cb26d0/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d666561747572652d666c6167732f6275696c642e796d6c3f6272616e63683d6d6173746572)](https://github.com/rasuvaeff/yii3-feature-flags/actions)[![Static Analysis](https://camo.githubusercontent.com/58006106184a49f05b34ff1eec5888634f7aafbab3b0cd7ffee275638f07d11c/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d666561747572652d666c6167732f7374617469632d616e616c797369732e796d6c3f6272616e63683d6d6173746572266c6162656c3d737461746963253230616e616c79736973)](https://github.com/rasuvaeff/yii3-feature-flags/actions)[![Psalm Level](https://camo.githubusercontent.com/c05996788ae434d070def332e4d8bd454fc358273de444e9e0d1dcd318917340/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5073616c6d2d312d626c75652e737667)](https://github.com/rasuvaeff/yii3-feature-flags/actions)[![PHP](https://camo.githubusercontent.com/ad92b7dbbd3d7af0785509148cc5c79ac83cd5b143fd4f753cd7fb0e3d994129/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f796969332d666561747572652d666c6167732f706870)](https://packagist.org/packages/rasuvaeff/yii3-feature-flags)[![License](https://camo.githubusercontent.com/2b6b8eff436b373501fb3bde3207a309cadaada039d0aa3fc7b230e74d89cad7/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7261737576616566662f796969332d666561747572652d666c6167732e737667)](LICENSE.md)[Русская версия](README.ru.md)

Feature flags, kill switches and percentage rollout for Yii3 applications.

Stateless core — storage backends are separate packages. Deterministic rollout via SHA-256 hash. Works with Yii3 config-plugin or standalone.

> Using an AI coding assistant? [llms.txt](llms.txt) has a compact API reference you can give to the LLM to help it work with this package.

Requirements
------------

[](#requirements)

- PHP 8.3+

Installation
------------

[](#installation)

```
composer require rasuvaeff/yii3-feature-flags
```

Usage
-----

[](#usage)

### Basic flag check

[](#basic-flag-check)

```
use Rasuvaeff\Yii3FeatureFlags\FeatureFlags;
use Rasuvaeff\Yii3FeatureFlags\FlagContext;

if ($featureFlags->isEnabled(
    flag: 'new-checkout',
    context: FlagContext::forUser(userId: $userId),
)) {
    // New checkout flow.
}
```

### Configuration

[](#configuration)

In your application config (`config/params.php`):

```
return [
    'rasuvaeff/yii3-feature-flags' => [
        'flags' => [
            'new-checkout' => [
                'enabled' => true,
                'salt' => 'new-checkout-v1',
                'rollout' => 25,
                'killSwitch' => false,
                'environments' => ['production'],
            ],
        ],
    ],
];
```

### Configuration options

[](#configuration-options)

KeyTypeDefaultDescription`enabled``bool``true`Master switch for the flag`salt``string`flag nameHash salt for deterministic rollout`rollout``int``100`Percentage of subjects to include (0..100)`killSwitch``bool``false`Immediately disable the flag, overrides all rules`environments``list``[]`Restrict to specific environments (empty = all)### FlagContext

[](#flagcontext)

```
// By user ID
$context = FlagContext::forUser(userId: 'user-42');

// By tenant ID
$context = FlagContext::forTenant(tenantId: 'tenant-1');

// By environment
$context = FlagContext::forEnvironment(environment: 'production');

// Combined
$context = FlagContext::forUser(userId: 'user-42')
    ->withEnvironment(environment: 'production');
```

### Forced values

[](#forced-values)

Use forced values for QA/debug overrides on existing flags:

```
$context = FlagContext::forUser(userId: 'user-42')
    ->withForcedFlag(flag: 'new-checkout', enabled: true);

$featureFlags->isEnabled(flag: 'new-checkout', context: $context);
```

A forced value never re-enables a flag that has its kill switch active — the kill switch wins.

### Strict mode

[](#strict-mode)

Unknown flags return `false` by default. Enable strict mode to throw instead:

```
$featureFlags = new FeatureFlags(
    provider: $provider,
    strictMode: true,
);
```

### Evaluation result

[](#evaluation-result)

Get detailed information about why a flag is enabled or disabled:

```
$result = $featureFlags->evaluate(
    flag: 'new-checkout',
    context: FlagContext::forUser(userId: $userId),
);

$result->isEnabled();      // bool
$result->getReason();      // EvaluationReason enum
$result->getFlagName();    // string
```

`EvaluationReason` carries a single machine-readable reason instead of a set of overlapping booleans:

Case`enabled`When`Enabled``true`Flag on, no targeting/rollout exclusion`Disabled``false``enabled: false` on the flag`KillSwitch``false``killSwitch: true` overrides everything`RolloutExcluded``false`Subject outside the rollout bucket`EnvironmentExcluded``false`Context environment not in the flag's allowlist`Forced`caller-set`FlagContext::withForcedFlag()` overrode the result`Unknown``false`Flag not registered (non-strict mode)```

### Kill switch

Set `killSwitch: true` in config to immediately disable a flag, overriding all
targeting, rollout and forced-value rules.

### Percentage rollout

Deterministic assignment using `sha256(salt . ':' . subjectId)`:

- Same `salt` + `subjectId` always produces the same result.
- Changing `salt` resets assignment (intentional re-randomization).
- Changing weights shifts boundaries; some subjects may change variant.

## Public API

| Class | Description |
|---|---|
| `FeatureFlags` | Facade service: `isEnabled()`, `isDisabled()`, `evaluate()`, `has()` |
| `Flag` | Immutable flag value object |
| `FlagConfig` | Config DTO for programmatic flag definitions |
| `FlagContext` | Evaluation context (userId, tenantId, environment) |
| `FlagProvider` | Read-only interface for flag sources |
| `WritableFlagProvider` | `extends FlagProvider`: adds `save(Flag)`, `remove(string)` |
| `ConfigFlagProvider` | Provider from PHP config arrays (read-only) |
| `FlagRegistry` | Named flag lookup |
| `FlagEvaluator` | Core evaluation logic |
| `PercentageRollout` | Deterministic percentage assignment |
| `EvaluationResult` | Detailed evaluation outcome (private constructor; 7 static factories) |
| `EvaluationReason` | String-backed enum of evaluation outcomes |
| `MetricsRecorder` | Interface for recording evaluation metrics |
| `NullMetricsRecorder` | No-op default implementation |

## Storage backends

The core wires only the `FeatureFlags` facade. The `FlagProvider` implementation
is supplied by **exactly one** provider — a storage backend or, for config-array
flags, the application. This keeps backends drop-in: install one and it is
wired automatically, with no `Duplicate key` config conflict.

| Package | Description |
|---|---|
| [`rasuvaeff/yii3-feature-flags-db`](https://github.com/rasuvaeff/yii3-feature-flags-db) | Database (yiisoft/db) with PSR-16 caching and migration |

Install a backend and you are done — it binds `FlagProvider` for you:

```bash
composer require rasuvaeff/yii3-feature-flags-db

```

### Writable backends

[](#writable-backends)

A backend may implement `WritableFlagProvider` to support programmatic flag CRUD (e.g. via an admin UI). `DbFlagProvider` and `CachedFlagProvider` in `yii3-feature-flags-db` both implement it; `ConfigFlagProvider` does not — it is read-only.

```
use Rasuvaeff\Yii3FeatureFlags\Flag;
use Rasuvaeff\Yii3FeatureFlags\WritableFlagProvider;

/** @var WritableFlagProvider $provider */
$provider->save(flag: new Flag(name: 'new-checkout', rollout: 25));
$provider->remove(name: 'old-checkout');
```

`save()` is an upsert keyed by the flag `name`. Implementations that decorate a cache (e.g. `CachedFlagProvider`) invalidate themselves after a successful write.

Metrics
-------

[](#metrics)

`FeatureFlags` accepts an optional `MetricsRecorder` as its last constructor argument. After each `evaluate()` call it receives the resulting `EvaluationResult` exactly once (never on the throw path in strict mode). The default is `NullMetricsRecorder`, which is a no-op — passing nothing is safe.

```
use Rasuvaeff\Yii3FeatureFlags\FeatureFlags;
use Rasuvaeff\Yii3FeatureFlags\MetricsRecorder;

$recorder = new class implements MetricsRecorder {
    #[\Override]
    public function recordEvaluation(\Rasuvaeff\Yii3FeatureFlags\EvaluationResult $result): void
    {
        // ship $result->getReason()->value to your metrics backend
    }
};

$featureFlags = new FeatureFlags(provider: $provider, recorder: $recorder);
```

Adapter packages (`-psr-logger`, `-prometheus`, …) may be added later; the core **does not** bind `MetricsRecorder` in its `config/di.php`, so installing an adapter next to the core will never trigger a `Duplicate key` error.

### Config-only setup

[](#config-only-setup)

Without a storage backend, define flags in `params.php` and bind `FlagProvider`to `ConfigFlagProvider` once in your application config (`config/common/di/*.php`):

```
use Rasuvaeff\Yii3FeatureFlags\ConfigFlagProvider;
use Rasuvaeff\Yii3FeatureFlags\FlagProvider;

/** @var array $params */

return [
    FlagProvider::class => [
        'class' => ConfigFlagProvider::class,
        '__construct()' => [
            'flags' => $params['rasuvaeff/yii3-feature-flags']['flags'],
        ],
    ],
];
```

Bind `FlagProvider` from a single source — installing two backends (or a backend plus a manual config binding) reintroduces the `Duplicate key` conflict.

Security
--------

[](#security)

- Flag names validated by regex (`/^[a-z][a-z0-9._-]*$/`).
- Rollout percentage validated (0..100 range).
- No user data is logged or stored by the core package.
- Kill switch provides emergency shutoff capability.

Examples
--------

[](#examples)

See [examples/](examples/) for runnable scripts. Examples are expected to execute without fatal errors and stay aligned with the documented public API.

Development
-----------

[](#development)

```
make install     # composer install
make build       # full gate: validate + cs + psalm + test
make cs-fix      # fix code style
make psalm       # static analysis
make test        # run tests
make test-coverage  # run coverage
make mutation       # mutation testing
make release-check  # build + rector + bc-check + mutation
```

`make test-coverage` and `make mutation` bootstrap `pcov` inside the `composer:2` container because the base image has no coverage driver.

License
-------

[](#license)

BSD-3-Clause. See [LICENSE.md](LICENSE.md).

###  Health Score

47

—

FairBetter than 93% of packages

Maintenance97

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community12

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~7 days

Total

3

Last Release

25d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/b0812d5572a7041dfe36e222d295b2e6dc55833a605350fcde58a51a5965ed30?d=identicon)[rasuvaeff](/maintainers/rasuvaeff)

---

Top Contributors

[![rasuvaeff](https://avatars.githubusercontent.com/u/1352718?v=4)](https://github.com/rasuvaeff "rasuvaeff (22 commits)")

---

Tags

continuous-deliveryfeature-flagsfeature-toggleskill-switchphprolloutyii3feature toggleyii3rolloutfeature-flagsKill switch

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-feature-flags/health.svg)

```
[![Health](https://phpackages.com/badges/rasuvaeff-yii3-feature-flags/health.svg)](https://phpackages.com/packages/rasuvaeff-yii3-feature-flags)
```

###  Alternatives

[flagception/flagception-bundle

Feature toggle bundle on steroids.

294.0M](/packages/flagception-flagception-bundle)[flagception/flagception

Feature toggle on steroids.

154.5M6](/packages/flagception-flagception)[ajgarlag/feature-flag-bundle

Provides a feature flag mechanism

1421.0k](/packages/ajgarlag-feature-flag-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
