rasuvaeff / yii3-feature-flags-ui
Admin UI panel for managing Yii3 feature flags
Package info
github.com/rasuvaeff/yii3-feature-flags-ui
pkg:composer/rasuvaeff/yii3-feature-flags-ui
Requires
- php: 8.3 - 8.5
- ext-ctype: *
- ext-json: *
- psr/container: ^2.0
- psr/event-dispatcher: ^1.0
- psr/http-factory: ^1.1
- psr/http-message: ^2.0
- psr/http-server-handler: ^1.0
- rasuvaeff/yii3-feature-flags: ^1.0
- yiisoft/data: ^2.0
- yiisoft/form-model: ^1.1
- yiisoft/html: ^3.13 || ^4.0
- yiisoft/request-body-parser: ^1.2
- yiisoft/router: ^3.1 || ^4.0
- yiisoft/user: ^2.0
- yiisoft/validator: ^2.5
- yiisoft/yii-dataview: ^1.1
- yiisoft/yii-view-renderer: ^7.4
Requires (Dev)
- ergebnis/composer-normalize: ^2.51
- friendsofphp/php-cs-fixer: ^3.95
- infection/infection: ^0.29
- maglnet/composer-require-checker: ^4.17
- nyholm/psr7: ^1.8
- phpunit/phpunit: ^11.5
- psalm/plugin-phpunit: ^0.19
- rector/rector: ^2.4
- roave/backward-compatibility-check: ^8.0
- vimeo/psalm: ^6.16
- yiisoft/router-fastroute: ^4.0
This package is auto-updated.
Last update: 2026-06-19 06:37:27 UTC
README
Admin UI panel for managing Yii3 feature flags.
Using an AI coding assistant? llms.txt contains a compact API reference you can share with the model.
A drop-in admin panel for rasuvaeff/yii3-feature-flags:
list flags in a sortable grid (with kill switch and disabled badges), create
and edit them (name, enabled, rollout, salt, kill switch, environments), delete
them, and emit FlagChanged events for audit trail / cache invalidation. Read-only
providers automatically get disabled controls.
Requirements
- PHP 8.3+
rasuvaeff/yii3-feature-flags^1.0 -Flag,FlagProvider,WritableFlagProvider- A writable provider backend (usually
rasuvaeff/yii3-feature-flags-db^1.0) bound toFlagProviderandWritableFlagProvider yiisoft/yii-view-renderer,yiisoft/router,yiisoft/useryiisoft/html,yiisoft/validator,yiisoft/form-model,yiisoft/data,yiisoft/yii-dataview- A concrete router implementation (e.g.
yiisoft/router-fastroute) - provided by your application - Bootstrap 5 CSS loaded by the host application (views use Bootstrap classes, no inline styles)
The list grid is rendered server-side from the application DI container
(FlagsGridFactory), so the host does not need to bootstrap WidgetFactory.
Installation
composer require rasuvaeff/yii3-feature-flags-ui
Usage
The package ships Yii3 config-plugin wiring (di, params). Add your params
to the merge chain:
use Rasuvaeff\Yii3FeatureFlagsUi\FlagRoutes; return [ FlagRoutes::PARAM_KEY => [ 'route_prefix' => '/admin/flags', 'layout' => null, 'views' => [ 'list' => '/abs/path/to/flags-list.php', 'edit' => '/abs/path/to/flags-edit.php', ], 'middlewares' => [ 'all' => [AuthMiddleware::class], ], // RequestBodyParser is added automatically to POST routes (store, update, delete). // Set 'body_parser' => false if your pipeline already applies it globally. ], ];
layout controls the shared wrapper. views.list and views.edit override only the corresponding templates; they do not replace the layout.
Bind the flag contracts to your provider. With rasuvaeff/yii3-feature-flags-db ^1.0
this is automatic — its config-plugin binds WritableFlagProvider and FlagProvider
to the same DbFlagProvider. For a custom backend, bind them yourself:
use Rasuvaeff\Yii3FeatureFlags\{FlagProvider, WritableFlagProvider}; use Rasuvaeff\Yii3FeatureFlagsDb\DbFlagProvider; return [ FlagProvider::class => DbFlagProvider::class, WritableFlagProvider::class => Reference::to(FlagProvider::class), ];
Routes
| Method | Path | Action | Default name |
|---|---|---|---|
| GET | {prefix} |
Yii\List\Action |
flags/list |
| GET | {prefix}/new |
Yii\Edit\Action::new() |
flags/create |
| GET | {prefix}/{name}/edit |
Yii\Edit\Action |
flags/edit |
| POST | {prefix}/new |
Yii\Update\Action::new() |
flags/store |
| POST | {prefix}/{name} |
Yii\Update\Action |
flags/update |
| POST | {prefix}/{name}/delete |
Yii\Delete\Action |
flags/delete |
middlewares.{all,list,edit,create,store,update,delete} — add middlewares per slot without forking the routes. RequestBodyParser is added automatically to the POST routes (store, update, delete); set 'body_parser' => false in params to opt out.
URLs and redirects are generated through the router (UrlGeneratorInterface) by route name; links stay correct under any prefix or subdomain. Override route_names in params when your app uses a different naming convention.
Flat-route wiring
Wire the bundled config/routes.php explicitly in configuration.php:
'routes' => 'vendor/rasuvaeff/yii3-feature-flags-ui/config/routes.php',
The route prefix, names and middlewares are read from params (FlagRoutes::PARAM_KEY).
Group-based admin panel
Inside a Group (the typical approach for a shared-prefix admin area):
use Rasuvaeff\Yii3FeatureFlagsUi\FlagRoutes; use Yiisoft\Router\Group; Group::create(prefix: '/admin')->routes( ...FlagRoutes::fromParams($params), );
fromParams() reads prefix, names, middlewares and body-parser opt-out from
$params[FlagRoutes::PARAM_KEY], so route registration and FlagUrls URL generation
are always in sync.
For full control over names, use create() directly and add matching route_names to params:
FlagRoutes::create( prefix: '/flags', names: ['list' => 'admin/flags', 'edit' => 'admin/flags/edit'], middlewares: ['all' => [AuthMiddleware::class]], )
Authorization
The package does not enforce access control internally. Protect routes via
middlewares.all (or per-route keys). The package provides CurrentUser injection
for audit events only.
Public API
| Class | Description |
|---|---|
FlagRoutes |
Builds the 6 routes; fromParams($params) for group-based panels, create() for full control |
Yii\List\Action |
GET grid of all flags, with KILLED/OFF badges |
Yii\Edit\Action |
GET edit form for existing flag; ::new() for create form |
Yii\Update\Action |
POST validate + save; ::new() for create; re-render on invalid |
Yii\Delete\Action |
POST remove flag -> redirect to list |
Form\FlagForm |
Submitted edit input (name, enabled, rollout, salt, killSwitch, environments) |
Validation\FlagFormNormalizer |
Casts validated form to a Flag |
Renderer\TemplateRendererInterface |
Rendering seam (testable actions) |
Renderer\ViewTemplateRenderer |
Default renderer over WebViewRenderer |
Event\FlagChanged |
PSR-14 event after save/delete (name, operation, actor) |
Read-only providers
If your FlagProvider does not implement WritableFlagProvider:
config/routes.phpstill registers all routes; runtimeinstanceofchecks return 403 on POST.- List view shows a "Read-only provider" badge and the create button is hidden.
- Edit view disables all fields and shows a warning.
This lets ConfigFlagProvider (config-only flags) be browsed in the UI without write support.
Security
| Concern | Behaviour |
|---|---|
| Read-only providers | Update/Delete rejected with HTTP 403 |
| Unknown flag name | Edit returns 404, Update/Delete return 404 |
| Invalid input | Re-renders the edit page with HTTP 200, no write |
| Flag name injection | On edit existing, the submitted name is ignored; the route name is pinned |
| Kill switch warning | The edit form always renders the warning; users cannot disable the warning |
| CSRF | Enforced by your application middleware; the form emits a hidden _csrf field when a csrf_token request attribute is present |
| Output | All values pass through Yiisoft\Html\Html::encode() / Html widgets / GridView encoding |
Customising views
Override views.list and/or views.edit in params with absolute paths to your own templates. The templates receive the same variables as the bundled ones — see resources/views/.
The edit form uses input names scoped under Flag[...] (e.g. Flag[name], Flag[enabled]). Custom edit templates must preserve this scope for FlagForm::fromParsedBody() to work.
Flash messages are not built in — the package does not know about the host app's session. Subscribe to FlagChanged in your app to add flash notifications, cache invalidation, or audit trail entries.
Why FlagChanged Exists
The package emits FlagChanged after save/delete so the host app can react without
coupling itself to the UI actions. Typical uses are cache invalidation, audit
logging, metrics counters, and dependent reconfiguration. The actor field
carries the current user ID; null for guests.