Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Alea Laravel Package

wikimedia/alea

Alea is a PHP port of the Alea pseudo-random number generator, providing fast, seedable, deterministic random values. Useful for simulations, games, and tests where reproducible output matters.

View on GitHub
Deep Wiki
Context7

Getting Started

wikimedia/alea is a small, dependency-free PHP library from the Wikimedia Foundation for deterministic sampling — given an identifier and a rate, it decides whether that identifier is "in" or "out" of a sample, consistently. It ships no Laravel service provider, no config file, and no facade. That is the whole point: it is a primitive, and your job is to wrap it once and then use it everywhere.

Install:

composer require wikimedia/alea

Where to look first:

  1. vendor/wikimedia/alea/src/ — the whole library is a handful of classes; read them before writing any wrapper. The public surface is tiny and you should know it exactly.
  2. vendor/wikimedia/alea/README.md — for the constructor signature and rate semantics on your pinned version.
  3. The Gerrit repo (gerrit.wikimedia.org/g/mediawiki/libs/alea), not GitHub. The GitHub repo is an automated mirror; issues and patches live in Phabricator/Gerrit.

First use case — deterministic feature rollout. The canonical day-one win is replacing ad-hoc mt_rand(1, 100) <= $percent checks, which are non-deterministic per request and therefore produce a flickering experience for a single user:

use Wikimedia\Alea\Sampler;

$sampler = new Sampler(0.05); // 5% sample rate

if ($sampler->sample($request->user()->id)) {
    // this user is consistently in the 5% bucket, on every request
}

Because the decision is a pure function of (rate, key), the same user always lands in the same bucket — which is exactly what you want for feature flags, experiments, and log-volume reduction.

Implementation Patterns

1. Bind it once in a service provider

Never new Sampler(...) inside a controller. Put the rate in config and resolve it through the container:

// config/alea.php
return [
    'features' => [
        'new_checkout'    => (float) env('ALEA_NEW_CHECKOUT_RATE', 0.0),
        'async_webhooks'  => (float) env('ALEA_ASYNC_WEBHOOKS_RATE', 1.0),
    ],
];
// app/Providers/AleaServiceProvider.php
public function register(): void
{
    $this->app->singleton(SamplerFactory::class, fn () => new SamplerFactory(config('alea.features')));
}

Then a thin SamplerFactory returns a Sampler per feature name. This gives you one place to change rates, one place to audit them, and one place to stub them in tests.

2. Always key on something stable

The quality of the whole system depends on the key you feed it. Good keys: user ID, a UUID stored in a cookie, an account creation hash. Bad keys: now(), request()->ip() (changes on mobile networks), session()->getId() (regenerated on login), anything from random_bytes().

For anonymous traffic, mint a UUID in a long-lived cookie on first visit and use that as the key — otherwise logged-out users flip buckets on every session.

3. Middleware for per-request bucketing

Decide once per request, stash the decisions on the request, and let the rest of the stack read them:

public function handle($request, Closure $next)
{
    $key = $request->user()?->id ?? $request->cookie('visitor_id');

    $request->attributes->set('alea', $this->factory->decideAll((string) $key));

    return $next($request);
}

Downstream: $request->attributes->get('alea')['new_checkout']. This keeps the sampler out of your views and controllers and makes the decisions trivially loggable in one place (which you will want when someone asks "why did this user see the old checkout?").

4. Experiments with more than two arms

A sampler answers a boolean. For A/B/C, run independent samplers with cumulative rates, or derive a single bucketing value once and slice it yourself:

0.00 – 0.49 → control
0.50 – 0.79 → variant A
0.80 – 1.00 → variant B

Wrap this in an Experiment class that maps a key to an arm name. Record the arm in your analytics event alongside the user ID so the analysis is reproducible.

5. Sampling telemetry and logs

This is where the library quietly pays for itself. Before shipping a high-volume log or metric to an external sink, sample it:

if ($this->sampler->sample($event->id)) {
    Metrics::push($event);
}

At 1% you cut ingestion cost by two orders of magnitude and still get statistically valid rates — provided you sampled on a stable key and not on rand(). If you sample per event rather than per user, be explicit about it; per-event sampling is fine for counting, per-user sampling is mandatory for anything behavioural.

6. Queues and Artisan commands

Background work frequently needs to sample at much higher rates than web traffic (or the inverse — you may want to exclude sampled users from a backfill). Pass the same key through the job payload so the decision is stable between the web request and the queued job. In commands, use a stable key like the tenant ID or a batch ID, not the process PID.

7. Testing

Never let real sampling leak into tests. Bind a fake in TestCase::setUp():

$this->app->bind(SamplerFactory::class, fn () => new AlwaysSampleFactory());
$this->app->bind(SamplerFactory::class, fn () => new NeverSampleFactory());

Then write a small number of property tests against the real library — same key always yields the same answer; rate 0.0 never samples; rate 1.0 always samples; a large population at rate 0.1 lands near 10% — and keep everything else faked.

Gotchas and Tips

  • It is a mirror. GitHub PRs and issues will be ignored. Patches go to Gerrit, bugs to Phabricator. Budget for that if you plan to fix something rather than work around it.
  • Not a CSPRNG. Do not use it for password reset tokens, API keys, nonces, or anything security-relevant. Sampling only.
  • Verify the constructor signature against your pinned version. The library is small and has had few releases; the rate parameter has moved between int weights and float rates across Wikimedia libraries. Read the source in vendor/ once and write a single factory wrapper so a future signature change touches one file.
  • Floats at the boundaries. 0.0 and 1.0 are edge cases that have historically been off-by-one bugs in weighted samplers. Assert both explicitly in tests, and prefer 0.0 over 0.00001 for "off" and 1.0 over 0.999 for "on".
  • config:cache and provider ordering. Read config('alea.*') inside the closure passed to singleton(), not as a constructor argument evaluated at boot. Otherwise a cached config baked at deploy time silently pins yesterday's rates.
  • Octane / long-running workers. Register the sampler as a singleton, but never let it accumulate per-request state. If you memoise decisions in a static array keyed by user, you will leak memory across requests and serve stale buckets.
  • Rate changes are not retroactive in effect. Dropping a rate from 10% to 5% does not remove the original 10% from their bucket — it selects a different subset. If you roll back a feature, roll back to 0.0, not to a lower rate, unless you actually want a churn of users flipping in and out.
  • Skewed keys break the maths. Auto-increment IDs are fine. Sequential IDs with heavy gaps per shard, or UUIDs where the high bits are constant, are usually fine too — but if your sample looks wildly off from the configured rate, the first thing to check is your key distribution, not the library.
  • Small populations. At a 1% rate, a team of 20 engineers will see roughly zero samples locally unless they use a fixed key. Build a ALEA_FORCE=1 / ALEA_FORCE=0 override into your factory early; you will use it daily.
  • Log the decision, not just the outcome. When a support ticket says "the new checkout is broken for me", you want to answer it from an artisan command, not by re-running the sampler and hoping the key matches.
  • Don't wrap it in a facade. A Sampler is trivially injectable and trivially fakeable. A facade adds indirection and makes the stable-key discipline harder to enforce.
  • Low bus factor. Three stars and zero dependents on Packagist means you own this dependency in practice. It is ~MIT, small, and stable enough that pinning a major version and reading the source once is genuinely low-risk — but pin it, and don't rely on it gaining features.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib