rasuvaeff / yii3-ab-testing-db
Database-backed experiment provider for Yii3 A/B testing
Requires
- php: 8.3 - 8.5
- ext-json: *
- psr/simple-cache: ^3.0
- rasuvaeff/yii3-ab-testing: ^1.4
- yiisoft/db: ^2.0
- yiisoft/db-migration: ^2.0
Requires (Dev)
- ergebnis/composer-normalize: ^2.51
- friendsofphp/php-cs-fixer: ^3.95
- infection/infection: ^0.33
- maglnet/composer-require-checker: ^4.17
- rector/rector: ^2.4
- roave/backward-compatibility-check: ^8.0
- testo/bridge-infection: ^0.1.6
- testo/testo: ^0.10.25
- vimeo/psalm: ^6.16
- yiisoft/cache: ^3.2
- yiisoft/db-sqlite: ^2.0
- yiisoft/injector: ^1.2
- yiisoft/test-support: ^3.0
This package is auto-updated.
Last update: 2026-07-25 13:28:08 UTC
README
Database-backed experiment provider for Yii3 A/B testing. Implements the
ExperimentProvider interface from rasuvaeff/yii3-ab-testing and reads
experiment configuration from a database table in a single query, so experiments
can be toggled and reweighted at runtime without a deploy.
Using an AI coding assistant? llms.txt contains a compact API reference you can ingest in your prompt context.
Requirements
- PHP 8.3+
rasuvaeff/yii3-ab-testing^1.0yiisoft/db^2.0yiisoft/db-migration^2.0 (ships the table migration)- a PSR-16 cache implementation — required transitively by
yiisoft/db2.0 (e.g.yiisoft/cache)
Installation
composer require rasuvaeff/yii3-ab-testing-db
With Yii3 config-plugin this package binds ExperimentProvider automatically — do
not also bind ExperimentProvider in your application or another backend, or
yiisoft/config reports a Duplicate key error.
Database schema
Create the ab_experiments table (adjust types for your RDBMS):
CREATE TABLE ab_experiments ( name VARCHAR(190) PRIMARY KEY, enabled BOOLEAN NOT NULL DEFAULT TRUE, salt VARCHAR(190) NOT NULL DEFAULT '', fallback_variant VARCHAR(190) NOT NULL DEFAULT '', variants TEXT NOT NULL DEFAULT '{}' );
| Column | Type | Default | Description |
|---|---|---|---|
name |
VARCHAR(190) PK |
— | Experiment name (core regex: /^[a-z][a-z0-9_-]*$/) |
enabled |
BOOLEAN |
true |
Disabled experiment returns the fallback variant |
salt |
VARCHAR(190) |
'' |
Empty string falls back to the experiment name |
fallback_variant |
VARCHAR(190) |
'' |
Must be one of the variants keys |
variants |
JSON/TEXT |
'{}' |
JSON object {"variant": weight}, non-negative integer weights |
A row's variants looks like {"control":50,"green":50}. The total weight must
be greater than zero and fallback_variant must match one of the keys, or the row
is rejected with InvalidExperimentRowException.
Migration
Register the bundled migration by namespace — no vendor paths:
// config/common/di/migration.php use Yiisoft\Db\Migration\Service\MigrationService; return [ MigrationService::class => [ 'setSourceNamespaces()' => [[ 'App\\Migration', 'Rasuvaeff\\Yii3AbTestingDb\\Migration', ]], ], ];
./yii migrate:up ./yii migrate:down --limit=1
Set the table name in params — the same value reaches the migration and
DbExperimentProvider:
// config/common/params.php 'rasuvaeff/yii3-ab-testing-db' => [ 'table' => 'my_ab_experiments', 'table_prefix' => '', // prepended to `table`; e.g. 'rsv_' → rsv_my_ab_experiments ],
Both bundled migrations (M260610000000CreateAbExperimentsTable and
M260619000001AddTargetingToAbExperiments) take the same table name, so the
CREATE and the later ALTER can no longer target different tables.
Do not configure the migration through the DI container.
M...::class => ['__construct()' => ['table' => ...]]does not work: the migration is built byInjector::make(), which resolves arguments by type and never reads a container definition keyed by the migration's own class. Worse, adding that definition makes the container fatal at build time in every request, because the class is not autoloadable until the migration runner requires it. That recipe was documented in 1.x; it never worked.
Usage
Basic DB provider
use Rasuvaeff\Yii3AbTesting\AbTesting; use Rasuvaeff\Yii3AbTesting\WeightedHashAssignmentStrategy; use Rasuvaeff\Yii3AbTestingDb\DbExperimentProvider; $provider = new DbExperimentProvider( db: $connection, // yiisoft/db ConnectionInterface table: 'ab_experiments', // optional, default is 'ab_experiments' ); $ab = new AbTesting(provider: $provider, strategy: new WeightedHashAssignmentStrategy()); if ($ab->is(experiment: 'checkout-button', variant: 'green', subjectId: (string) $userId)) { // green variant }
With PSR-16 caching
getExperiments() runs on every registry build (per request). Without caching that
is a DB query per request — wrap the provider in CachedExperimentProvider:
use Rasuvaeff\Yii3AbTestingDb\CachedExperimentProvider; $cached = new CachedExperimentProvider( inner: $provider, cache: $psr16Cache, // PSR-16 CacheInterface ttl: 60, // seconds ); $ab = new AbTesting(provider: $cached, strategy: new WeightedHashAssignmentStrategy());
Clear cache
$cached->clear(); // removes cached experiments, next call reloads from DB
API reference
| Class | Description |
|---|---|
DbExperimentProvider |
Reads all experiments from DB in one SELECT * |
CachedExperimentProvider |
PSR-16 decorator, caches the entire experiment set with TTL |
InvalidExperimentRowException |
Thrown when a DB row has invalid structure or yields an invalid experiment |
Security
- Assignment hashing, fallback handling, forced/disabled logic remain in the core package — the DB adapter is only a configuration source.
- Invalid row data (missing columns, malformed
variantsJSON, wrong types, negative weights, invalid experiment name, unknown fallback, zero total weight) throwsInvalidExperimentRowExceptioninstead of silently mis-assigning. Core validation errors are wrapped, so callers only need to catch one exception type. - No SQL injection risk: the table name is quoted via the yiisoft/db quoter.
- Reweighting shifts buckets. Safe to flip
enabled(kill switch). Changing weights or the variant set shifts bucket boundaries and reshuffles subjects — useyii3-ab-testing-websticky assignment to pin subjects across such changes.
Examples
See examples/ for runnable scripts.
Development
composer build # full gate: validate + normalize + cs + psalm + test composer cs:fix # auto-fix code style composer psalm # static analysis composer test # run tests
License
BSD-3-Clause. See LICENSE.md.