rasuvaeff/yii3-ab-testing-db

Database-backed experiment provider for Yii3 A/B testing

Maintainers

Package info

github.com/rasuvaeff/yii3-ab-testing-db

pkg:composer/rasuvaeff/yii3-ab-testing-db

Transparency log

Statistics

Installs: 0

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v2.0.0 2026-07-25 13:28 UTC

This package is auto-updated.

Last update: 2026-07-25 13:28:08 UTC


README

Stable Version Total Downloads Build Static Analysis PHP License Русская версия

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.0
  • yiisoft/db ^2.0
  • yiisoft/db-migration ^2.0 (ships the table migration)
  • a PSR-16 cache implementation — required transitively by yiisoft/db 2.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 by Injector::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 variants JSON, wrong types, negative weights, invalid experiment name, unknown fallback, zero total weight) throws InvalidExperimentRowException instead 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 — use yii3-ab-testing-web sticky 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.