Download the PHP package rasuvaeff/yii3-ab-testing without Composer

On this page you can find all versions of the php package rasuvaeff/yii3-ab-testing. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package yii3-ab-testing

rasuvaeff/yii3-ab-testing

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

Deterministic A/B testing for Yii3 applications. Stateless assignment, weighted variants, forced variant for QA, explicit exposure/conversion tracking.

Using an AI coding assistant? llms.txt has a compact API reference you can pass as context. Projects using the llm/skills Composer plugin also get this package's agent skill synced into .agents/skills/ automatically on install.

Assembling a combination? docs/integration.md walks the eight axes — where definitions live, how events reach analytics, who the subject is, stickiness, SSR vs SPA, operations, reading results.

Requirements

Installation

Upgrading from 1.x? See UPGRADE.md.

Usage

Configure experiments

Experiment definitions come from an ExperimentProvider. ConfigExperimentProvider reads a static array; a storage backend (e.g. yii3-ab-testing-db) supplies a database-backed provider so experiments can be toggled at runtime without a deploy. Config definitions receive a deterministic configurationId hash. Runtime providers may pass an explicit string ID (for example, a database revision) to Experiment; every Assignment carries it for analytics and exposure deduplication.

Assign variant

Assigning an experiment that is not defined throws Exception\InvalidExperimentException; forcing a variant the experiment does not have throws Exception\InvalidVariantException. The loaded experiment set is inspectable via $ab->getRegistry() — an ExperimentRegistry with get(), has(), all() and reset(). The registry is lazy: the ExperimentProvider is queried on first access and memoized afterwards.

Forced variant (QA)

Why this variant: decision reason and source

Every Assignment answers two independent questions.

Question Field Values
Why this variant rather than the hash bucket? reason assigned, forced, fallback_disabled, fallback_targeting_mismatch
Where did the value come from? source computed, store

They are separate on purpose: a sticky-served forced variant is a legitimate combination, and a disabled experiment must stay distinguishable from a targeting miss — with a single flag both look like "fallback".

Only assigned counts as experiment participation; reports exclude everything else. DecisionReason::isAnalyzable() states that rule in code.

Track exposure and conversion

Both methods return the event they recorded — an ExposureEvent or a ConversionEvent carrying eventId, occurredAt, the decision, the experiment revision, the environment and the allow-listed dimensions. The identifier is the deduplication key of the whole pipeline: a delivery retried after an uncertain outcome carries the same value, so storage collapses the duplicate.

The conversion goal must contain at least one non-whitespace character; an invalid goal is rejected when the event is constructed, before any tracker runs.

Conversion in a later request

Re-resolving the assignment at conversion time can return a different variant than the visitor saw — a reweight or a salt change in between is enough. Carry a receipt instead:

AssignmentReceipt is deliberately small — it holds no environment or dimensions, because it travels in size-capped cookies and a conversion records the context of its own request anyway. fromArray() re-validates every field: transport data is never trusted, and unknown enum values are rejected.

Analytics dimensions

Context attributes reach storage only through an AnalyticsContextPolicy. The default allows nothing, so an attribute added for targeting cannot leak into analytics by accident.

Anything not listed is dropped, not redacted. Redaction keeps the column and replaces the value with [redacted].

Event identifiers

EventIdGenerator mints the event identity. The default Uuid7EventIdGenerator has no dependencies, so the package works straight after composer require, and UUIDv7 sorts by time — useful as a storage sort prefix. Adapters for the two common libraries ship alongside it:

Generator Requires
Uuid7EventIdGenerator (default) nothing
SymfonyUidEventIdGenerator symfony/uid
RamseyUuidEventIdGenerator ramsey/uuid

Nothing is selected automatically — bind the one you want. The format is not part of the contract: the interface returns a string and the analytics column is a string, so ULIDs, snowflakes or your own keys are equally valid.

Attribution contract

Reporting lives in the analytics package, but the rules that decide what the numbers mean are fixed here so every backend answers the same way.

A conversion counts for an exposure when it happens within the window after it. FirstOnly is the default because conversion rate is a share of subjects: one visitor converting ten times must not outweigh ten visitors converting once.

Assignment context (optional)

Pass an AssignmentContext to attribute metrics by environment/segment. It is carried into the returned Assignment so trackers can read it. Context may control targeting eligibility, but never changes the deterministic hash bucket.

Yii3 integration

Package provides config/params.php and config/di.php via config-plugin. Override in your application:

The core wires only the AbTesting facade and the default WeightedHashAssignmentStrategy. It does not bind ExperimentProvider (the experiment source) nor ExposureTracker / ConversionTracker (the event sinks) — those keys are owned by exactly one source each, so installing a storage/tracker backend wires them with no Duplicate key conflict.

Experiment source (required)

AbTesting needs an ExperimentProvider. Without a storage backend, bind ConfigExperimentProvider once in your app config (config/common/di/*.php), reading the experiments params above:

Installing yii3-ab-testing-db binds ExperimentProvider for you (database-backed, runtime-editable) — drop the manual binding then. Bind it from a single source: a backend plus a manual binding reintroduces the yiisoft/config Duplicate key conflict.

Delivering events

Two delivery paths are supported, and both produce the same rows:

Path How Trade-off
Durable yii3-ab-testing-outbox → exporter → ClickHouse survives an analytics outage; needs a table and a worker
Log shipping core's logger sinks → Vector / Fluent Bit / Kafka no worker, no request-time network call; delivery is the collector's job

Do not write to analytics storage from the request path. Under PHP-FPM a per-request insert means many tiny writes and a network call inside the user's latency — that is why the direct ClickHouse writer was removed in 2.0.

CanonicalEventSerializer produces the wire format both paths share, and the logger sinks emit exactly its output under an event key:

Every value is scalar, and dimensions is a JSON string rather than a nested object — the outbox exporter rejects nested payload fields, and the two paths must stay byte-identical. A runnable collector config is in examples/vector.toml.

Tracking backends (optional)

To persist exposures/conversions, opt in by binding the tracker interface to a real implementation — either from a dedicated adapter package or once in your own app config (config/common/di/*.php):

Two ready-made sinks ship in core: LoggerExposureTracker / LoggerConversionTracker write each event as one structured PSR-3 log record (zero infrastructure, log level configurable). Like every tracker they are not bound by core config/di.php (one-source rule) — bind them in your app config:

Bind each interface from a single source. Installing two adapters that both bind ExposureTracker (or a backend plus a manual binding) reintroduces a yiisoft/config Duplicate key conflict — pick one, or compose them with the built-in CompositeExposureTracker / CompositeConversionTracker, bound once in your own app config:

Trackers that buffer events implement FlushableTracker; call flush() once at request end. The composite trackers implement it too and propagate the flush to every flushable inner tracker, so the application can flush through the bound tracker interface:

To emit at most one exposure for the same experiment, subject and configuration within a request, wrap the real sink with DeduplicatingExposureTracker. The wrapper has mutable request state: bind it request-scoped, or call reset() at the request boundary in a long-running worker. It forwards flush() to a flushable inner sink. assign() remains side-effect free.

Targeting (optional)

Restrict an experiment to a subset of subjects by attaching a TargetingRule. Subjects that don't match receive the fallback variant with isFallback === true and isTargetingMismatch() true. forcedVariant bypasses targeting.

Available built-in rules:

Class Matches when
EnvironmentTargetingRule context->getEnvironment() is in the given list
AttributeTargetingRule context->getAttribute($name) === $value (strict)
AndTargetingRule all nested rules match (short-circuit)
OrTargetingRule at least one nested rule matches (short-circuit)

ConfigExperimentProvider accepts the same tagged arrays used by the DB JSON representation (environment, attribute, and, or).

TargetingRuleCodecRegistry::decode() and encode() provide the shared config/DB representation. Register a custom TargetingRuleCodec in its constructor to add another tagged rule type; custom codecs are checked before the built-in codec. Decoding rejects targeting trees nested deeper than 64 levels with InvalidArgumentException.

Sticky variants (optional)

Deterministic assignment keeps a subject in the same variant only while weights are stable; changing weights or the variant set shifts bucket boundaries and reshuffles subjects. To pin a subject to a variant across such changes, persist the assignment through an AssignmentStore:

A plain store pins a subject forever — which is right while the experiment is stable and wrong the moment it is reweighted, because the subject keeps a variant drawn from boundaries that no longer exist. Stores that can tell configurations apart implement the extension:

getForConfiguration() returns null when nothing is stored for that configuration — including when a variant is stored for a different one, which is what makes a reweight drop stale pins instead of replaying them. Both the signed-cookie store in yii3-ab-testing-web and DbAssignmentStore in yii3-ab-testing-db implement it. The interface lives here rather than in one adapter because two sibling adapters must not depend on each other to share a contract.

AbTesting::assign() stays pure — sticky resolution is a separate layer. Cookie/session implementations and a SubjectIdMiddleware for stable anonymous identity ship in yii3-ab-testing-web. An assignment served from a store carries isSticky = true so trackers can tell it apart from a fresh deterministic one. Both AbTesting and the web package's sticky resolver implement AssignmentResolver, whose resolve() signature mirrors assign().

Worker runtimes (RoadRunner, Swoole)

The experiment set is memoized per ExperimentRegistry instance. In a long-running worker the AbTesting service survives across requests, so the core's config/di.php registers a reset hook for yiisoft/di's StateResetter: runtimes that reset container state between requests re-read the ExperimentProvider on the next request, and a kill switch flipped in the source takes effect without a worker restart. In classic PHP-FPM nothing changes — the service is rebuilt per request anyway.

Assignment algorithm

This is bucketing algorithm v1. Its hash input, digest slice, variant sorting and boundary rules are a compatibility contract and will not change in a patch or minor release. Any future incompatible algorithm requires an explicit version and a major release.

Variants sorted by key. Cumulative weight boundaries determine assignment.

Every weight must be a non-negative integer, and Experiment enforces it in the constructor — a fractional, numeric-string or negative weight throws InvalidExperimentException. A negative one is the reason the check exists: the total can still clear > 0, but the cumulative boundary goes backwards and the preceding variant becomes unreachable, so the experiment quietly runs a distribution nobody configured. Zero stays valid: it keeps a variant defined while routing no traffic to it.

The total is re-checked after summing: individually valid weights whose sum exceeds PHP_INT_MAX make array_sum() return a float, which breaks the bucketing modulo. Both Experiment and a direct WeightedHashAssignmentStrategy call reject such a map instead of failing at assignment time.

Guarantees

Security

Examples

See examples/ for complete usage scenarios.

Development

make test-coverage and make mutation bootstrap pcov inside the composer:2 container because the base image has no coverage driver.

License

BSD-3-Clause. See LICENSE.md.


All versions of yii3-ab-testing with dependencies

PHP Build Version
Package Version
Requires php Version 8.3 - 8.5
psr/clock Version ^1.0
psr/log Version ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package rasuvaeff/yii3-ab-testing contains the following files

Loading the files please wait ...