PHPackages                             rasuvaeff/yii3-ab-testing-web - 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-ab-testing-web

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

rasuvaeff/yii3-ab-testing-web
=============================

Cookie identity and sticky-variant store for Yii3 A/B testing

v1.0.2(4w ago)00BSD-3-ClausePHPPHP 8.3 - 8.5CI passing

Since Jun 12Pushed 1w agoCompare

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

READMEChangelogDependencies (29)Versions (5)Used By (0)

rasuvaeff/yii3-ab-testing-web
=============================

[](#rasuvaeffyii3-ab-testing-web)

[![Stable Version](https://camo.githubusercontent.com/3b8892dc86576ec2a4ca373c344ea91257b0e6c072537f05fd5acf65e0bcf0f9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7261737576616566662f796969332d61622d74657374696e672d7765622e7376673f6c6162656c3d737461626c65)](https://packagist.org/packages/rasuvaeff/yii3-ab-testing-web)[![Total Downloads](https://camo.githubusercontent.com/f7be3fe290106e9f650579d5e62fb236090fa32b62500eb2651858b9e15538d3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7261737576616566662f796969332d61622d74657374696e672d7765622e737667)](https://packagist.org/packages/rasuvaeff/yii3-ab-testing-web)[![Build](https://camo.githubusercontent.com/55d5a6e45a632c87d245cb27d7cc3fe3ec0ee7bf7d854fb50760a007dec6fc3f/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d61622d74657374696e672d7765622f6275696c642e796d6c3f6272616e63683d6d6173746572)](https://github.com/rasuvaeff/yii3-ab-testing-web/actions)[![Static Analysis](https://camo.githubusercontent.com/c139cb85f435bc06a5a7fce4e3c211ba02564313007c9d3e42678ac36927f394/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f7261737576616566662f796969332d61622d74657374696e672d7765622f7374617469632d616e616c797369732e796d6c3f6272616e63683d6d6173746572266c6162656c3d737461746963253230616e616c79736973)](https://github.com/rasuvaeff/yii3-ab-testing-web/actions)[![PHP](https://camo.githubusercontent.com/d845009a7baea075384c2b864956bec7531a9139dfc3bfa6f85bf8d4855cb9e6/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f646570656e64656e63792d762f7261737576616566662f796969332d61622d74657374696e672d7765622f706870)](https://packagist.org/packages/rasuvaeff/yii3-ab-testing-web)[![License](https://camo.githubusercontent.com/ae0e8750f8bb0754db61d6b77ed23cc07c42302e738b529730e668b45f9f1720/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f7261737576616566662f796969332d61622d74657374696e672d7765622e737667)](LICENSE.md)[Русская версия](README.ru.md)

Web identity and sticky-variant layer for Yii3 A/B testing. Gives every visitor a stable subject id (so deterministic assignment holds across visits) and, when you need it, pins a subject to a variant across weight changes via a signed cookie.

> Using an AI coding assistant? [llms.txt](llms.txt) contains a compact API reference you can ingest in your prompt context.

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

[](#requirements)

- PHP 8.3+
- `rasuvaeff/yii3-ab-testing` ^1.2 (adds `AssignmentStore` and `Assignment::isSticky`)
- `yiisoft/cookies` ^1.2
- a PSR-7 implementation (e.g. `nyholm/psr7`) and a PSR-15 stack

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

[](#installation)

```
composer require rasuvaeff/yii3-ab-testing-web
```

Identity vs stickiness
----------------------

[](#identity-vs-stickiness)

Assignment is deterministic in `subjectId` (`sha256(salt:subjectId)`), so a stable id alone keeps a visitor in the same variant across visits — no variant is stored. Two cookie roles solve two different problems:

NeedUseA stable id for anonymous visitors`SubjectIdMiddleware` (cookie `ab_id`)Keep a variant even after weights/variants change`CookieAssignmentStore` + `StickyAssignmentResolver`A logged-in user already has a stable id (`userId`) — set it as the request attribute upstream and the middleware leaves it alone.

Subject identity middleware
---------------------------

[](#subject-identity-middleware)

Add `SubjectIdMiddleware` to your PSR-15 stack. It resolves the subject id and exposes it as a request attribute (`ab.subjectId` by default):

1. if the attribute is already set (an upstream auth middleware put `userId` there) it is kept — no cookie;
2. otherwise the `ab_id` cookie is reused — only when its value matches the generated format (32 lowercase hex chars); a tampered or oversized value is discarded and regenerated;
3. otherwise a new opaque id is generated and a long-lived `HttpOnly`, `SameSite=Lax` cookie is set.

```
use Rasuvaeff\Yii3AbTestingWeb\SubjectIdMiddleware;

$middleware = new SubjectIdMiddleware(); // defaults: cookie 'ab_id', attribute 'ab.subjectId'

// in your action/handler:
$subjectId = $request->getAttribute('ab.subjectId');
$assignment = $ab->assign(experiment: 'checkout-button', subjectId: $subjectId);
```

For most experiments this is all you need.

Sticky variants
---------------

[](#sticky-variants)

Changing weights or the variant set shifts bucket boundaries and reshuffles subjects. To pin a subject across such changes, resolve through a `CookieAssignmentStore` (a signed `{experiment: variant}` cookie) and a `StickyAssignmentResolver`. Because the store is request-scoped, wire it in a thin middleware that reads the cookie, exposes the store, and writes it back:

```
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Rasuvaeff\Yii3AbTestingWeb\CookieAssignmentStore;
use Yiisoft\Cookies\CookieSigner;

final class StickyCookieMiddleware implements MiddlewareInterface
{
    public function __construct(private CookieSigner $signer) {}

    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
    {
        $store = CookieAssignmentStore::fromRequest($request, $this->signer);
        $response = $handler->handle($request->withAttribute('ab.store', $store));

        return $store->applyToResponse($response);
    }
}
```

Then in your action:

```
use Rasuvaeff\Yii3AbTestingWeb\StickyAssignmentResolver;

$store = $request->getAttribute('ab.store');                 // CookieAssignmentStore
$resolver = new StickyAssignmentResolver($ab, $store);

$assignment = $resolver->resolve(
    experiment: 'checkout-button',
    subjectId: $request->getAttribute('ab.subjectId'),
);
// first time: assigned and stored; later: the stored variant is returned
```

`StickyAssignmentResolver` keeps `AbTesting::assign()` pure: a forced variant bypasses the store, a disabled experiment returns its fallback (the kill switch always wins and nothing is stored), and a stored variant that is no longer part of the experiment is re-assigned.

API reference
-------------

[](#api-reference)

ClassDescription`SubjectIdMiddleware`PSR-15 middleware; stable subject id + `ab_id` cookie`CookieAssignmentStore``AssignmentStore` over one signed cookie; `fromRequest()` / `applyToResponse()``StickyAssignmentResolver`get-or-assign over `AbTesting` + any `AssignmentStore`Security &amp; privacy
----------------------

[](#security--privacy)

- The subject id is an opaque 128-bit token (`random_bytes`), not a UUID, and carries no personal data — but it is a persistent identifier. Set the cookie only after consent where the law requires it.
- The sticky cookie is signed (`yiisoft/cookies` `CookieSigner`); a missing, unsigned, tampered, or malformed cookie is ignored and yields an empty store — never a partial or attacker-controlled variant map. Provide a strong signing key.
- Cookies are `HttpOnly`, `SameSite=Lax`, and `Secure` by default.
- The cookie is browser-scoped: the `$subjectId` argument of the store is ignored. A visitor who was anonymous then logged in keeps the variants from their anonymous identity.

Examples
--------

[](#examples)

See [examples/](examples/) for a runnable script (no server required).

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

[](#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
-------

[](#license)

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

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance97

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity54

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 ~9 days

Total

3

Last Release

28d 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 (12 commits)")

---

Tags

ab-testingcookiesmiddlewarephppsr-15sticky-variantsyii3middlewarecookieyii3experimentstickyab-testing

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/rasuvaeff-yii3-ab-testing-web/health.svg)

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

###  Alternatives

[cakephp/cakephp

The CakePHP framework

8.8k19.5M1.8k](/packages/cakephp-cakephp)[cakephp/authentication

Authentication plugin for CakePHP

1214.1M107](/packages/cakephp-authentication)[typo3/cms

TYPO3 CMS is a free open source Content Management Framework initially created by Kasper Skaarhoj and licensed under GNU/GPL.

1.2k1.9M122](/packages/typo3-cms)[flarum/core

Delightfully simple forum software.

211.4M2.4k](/packages/flarum-core)[typo3/cms-core

TYPO3 CMS Core

3713.2M5.2k](/packages/typo3-cms-core)[xima/xima-typo3-frontend-edit

Frontend Edit - This extension provides an edit button for editors within frontend content elements.

1414.3k](/packages/xima-xima-typo3-frontend-edit)

PHPackages © 2026

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