anbui / laravel-ab-experiments
Laravel A/B experiments
Requires
- php: ^8.2
- laravel/framework: ^12.0
Requires (Dev)
- orchestra/testbench: ^10.0
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
This package is not auto-updated.
Last update: 2026-08-12 18:39:05 UTC
README
Пакет для A/B тестирования в Laravel.
Что умеет пакет
- определяет, участвует ли пользователь в эксперименте;
- назначает вариант по правилам и весам;
- сохраняет назначение (sticky), чтобы вариант не "прыгал" между запросами;
- читает winner-state через быстрый файловый кеш и хранит консистентно в БД.
Установка
composer require anbui/laravel-ab-experiments
После установки:
php artisan vendor:publish --provider="Anbui\LaravelABEx\LABExServiceProvider"
php artisan migrate
Использование
Ниже минимальный путь запуска:
- описать класс эксперимента;
- зарегистрировать класс в
config/labex_conf.php; - получить вариант через
LABEx::forKey(...)->getVariant().
Минимально рабочий класс эксперимента:
<?php
declare(strict_types=1);
namespace App\AB;
use Anbui\LaravelABEx\Contracts\VariantRuleBuilderInterface;
use Anbui\LaravelABEx\Contracts\EligibilityRuleBuilderInterface;
use Anbui\LaravelABEx\Domain\Experiments\Experiment;
use Anbui\LaravelABEx\Domain\Variant\Rule\WeightedRule;
final class CheckoutExperiment extends Experiment
{
public static function key(): string
{
return 'checkout_test';
}
public function variants(): array
{
return [
'control',
'variant_a',
// можно добавить больше вариантов
];
}
public function defaultVariant(): string
{
return 'control';
}
public function eligibility(EligibilityRuleBuilderInterface $builder): void
{
// В текущем API субъект задается строковым ключом forKey(...),
// поэтому eligibility чаще всего задается вашими правилами по самому ключу.
}
public function definition(VariantRuleBuilderInterface $builder): void
{
$builder
->rule(new WeightedRule([
'control' => 50,
'variant_a' => 50,
]));
}
}
Как принимается решение по пользователю
Порядок работы:
- Проверяется winner-state (если победитель зафиксирован, возвращается он).
- Если winner-state нет, проверяется sticky assignment.
- Если sticky assignment нет, проверяется eligibility (
eligibility(...)). - Если пользователь не прошел eligibility, возвращается
defaultVariant(). - Если eligibility пройден, применяется
definition(...)и выбирается вариант по правилам.
Сценарии:
- если пользователь не проходит eligibility — он считается вне эксперимента и получает
defaultVariant(); - если проходит eligibility — пользователь участвует в эксперименте, и дальше выбирается вариант по правилам;
- если у пользователя уже есть sticky assignment, eligibility повторно не проверяется, и возвращается закрепленный вариант.
Если пользователь подошел в эксперимент, но не подошел ни под одно правило:
- возвращается
defaultVariant(); - пользователь остается участником эксперимента (
isInExperiment() === true); - assignment сохраняется с
rule_id = <ExperimentClass>::defaultVariant.
Важно: defaultVariant() должен возвращать одно из значений, перечисленных в variants().
Добавьте эксперимент в config/labex_conf.php:
'experiments' => [
\App\AB\CheckoutExperiment::class,
],
Работа с API
Получить вариант:
use Anbui\LaravelABEx\Facades\LABEx;
$variant = LABEx::forKey('checkout_test', 'user:42')->getVariant();
if (LABEx::forKey('checkout_test', 'user:42')->isVariant('variant_a')) {
// ...
}
if (LABEx::forKey('checkout_test', 'user:42')->isInExperiment()) {
// пользователь участвует в эксперименте
}
Рекомендуемый паттерн в коде контроллера/сервиса:
$subjectKey = 'user:' . (string) $user->id;
$experiment = LABEx::forKey('checkout_test', $subjectKey);
if (!$experiment->isInExperiment()) {
// пользователь не участвует в эксперименте
}
$variant = $experiment->getVariant();
Настройки пакета
Все настройки находятся в config/labex_conf.php.
Текущие доступные параметры:
experiments— список классов экспериментов. Ключ эксперимента берется изpublic static function key(): stringв самом классе.cache_path— директория, где хранится файловый кеш, в том числе winner-состояния.database.table_prefix— префикс таблиц пакета в БД (например,labex_).
Пример:
return [
'experiments' => [
\App\AB\CheckoutExperiment::class,
],
'cache_path' => storage_path('app/labex-cache'),
'database' => [
'table_prefix' => 'labex_',
],
];
Правила экспериментов
Описание доступных правил и примеры их использования:
На текущем этапе реализованы:
- базовый API
LABEx::forKey(...)->getVariant()иisVariant(...); - детерминированное распределение пользователя по вариантам;
- sticky assignment в таблице
labex_assignments; - winner-state в dual-store режиме: консистентное хранение в БД и быстрые чтения через файловый кеш.
Вклад в проект
Требования к разработке, проверкам и пул-реквестам описаны в CONTRIBUTING.md.