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

Murmurhash Laravel Package

lastguest/murmurhash

A fast, dependency-free PHP implementation of MurmurHash (MurmurHash3) for non-cryptographic hashing. Generate consistent 32/128-bit hashes for sharding, caching keys, bloom filters, and partitioning. Simple API, Composer-ready, and easy to drop into Laravel or any PHP project.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require lastguest/murmurhash

There is no service provider, config file, or facade — it is a plain static class. Nothing to publish or boot.

Look first at

  • vendor/lastguest/murmurhash/README.md — the canonical usage example (class name and method signatures) is here; confirm the exact static methods (Murmur::hash() for the 32-bit variant, and the 128-bit helper if you need it) before wiring it in, because the API is a single class with static methods and no interface contracts.
  • vendor/lastguest/murmurhash/src/ — the implementation is small enough to read in one sitting. Skim it once to confirm it is the x86 32-bit MurmurHash3 variant and how it treats the seed, since cross-language parity hinges on that.

First use case

Assign users/entities deterministically to buckets — the classic Laravel need for A/B tests, feature-flag rollout percentages, and cache sharding.

use lastguest\Murmur;

$bucket = hexdec(Murmur::hash('user:'.$userId, 1234)) % 100; // 0–99

if ($bucket < 10) {
    // 10% of users, stable for a given seed
}

That is the entire value proposition: a fast, stable, seedable bucket key. Do not reach for it when you need security (see Gotchas).

Implementation Patterns

Wrap it in a service, not static calls everywhere

Raw static calls scatter an unversioned, magic-seeded algorithm across your codebase. Bind a small service so the seed lives in config and is changeable in tests.

namespace App\Support;

use lastguest\Murmur;

class Bucketer
{
    public function __construct(private int $seed = 0) {}

    public function bucket(string $key, int $buckets = 100): int
    {
        // 7 hex chars = 28 bits, safe on 32-bit PHP too
        $n = hexdec(substr(Murmur::hash($key, $this->seed), 0, 7));

        return $n % $buckets;
    }
}
// app/Providers/AppServiceProvider.php
$this->app->singleton(Bucketer::class, fn () =>
    new Bucketer((int) config('services.experiments.seed', 0))
);

Feature rollout / experiment assignment

Seed per experiment so assignments are independent (feature:new-checkout + user:123 + experiment seed). One seed for everything collapses every experiment's buckets onto the same cohort, which silently invalidates your A/B results.

$inTreatment = $bucketer->bucket("exp:new-checkout:user:{$user->id}", 100)
    % 2 === 1;

Cache/shard keying

Use the hash to pick a shard or to compress long cache keys:

$shard = $bucketer->bucket($cacheKey, config('cache.shards'));
cache()->store("redis_{$shard}")->remember($cacheKey, $ttl, $callback);

Or shorten unbounded keys:

$key = 'rpt:'.Murmur::hash($reportDefinition, 0); // stable, fixed-length

Log sampling

Cheap, stable correlation sampling at the request/job level:

if ($bucketer->bucket($requestId, 1000) < 10) { // 1%
    Log::debug('sampled', ['request_id' => $requestId]);
}

Idempotency and deduplication keys

Derive a stable fingerprint from a job payload so retries collapse:

$key = 'job:'.Murmur::hash(json_encode($payload), 0);

Use this for routing or deduping, not for proving payload uniqueness — 32-bit collisions are reachable.

Model events / jobs

Apply it wherever determinism matters more than security: deterministic avatar colors, consistent ordering, tenant→worker assignment in queued jobs. Prefer passing the already-computed bucket through the job payload rather than recomputing, so a config/seed change mid-flight cannot split a user across treatments.

Gotchas and Tips

Not a cryptographic hash — ever

MurmurHash3 is fast and predictable by design. Never use it for passwords, tokens, signatures, or anything an attacker controls. Use Hash::make() / hash_hmac() / random_bytes() for those. If a reviewer sees Murmur::hash($password), that is a bug.

32-bit collisions are real

Murmur::hash() returns an 8-hex-character (32-bit) string. Birthday collisions become likely in the tens of thousands of distinct keys, so do not treat the value as a unique ID. For collision-resistant stable IDs over large sets, use the 128-bit helper (if present in your version) or xxh128/hash('sha256', ...).

hexdec() and 32-bit PHP

hexdec() on a full 8-char hash exceeds PHP_INT_MAX on 32-bit PHP and yields a float, which breaks % bucketing silently. Truncating to 7 hex chars (28 bits), as above, keeps the arithmetic integer-only. On 64-bit PHP the full 8 chars are fine, but the truncated version is portable — prefer it.

Seed portability across languages

Seeds and the exact variant matter. PHP's implementation is the 32-bit x86 variant; Guava/Java's murmur3_32 should agree, but the 128-bit variants and things like the Kafka partitioner (which uses murmur2, not murmur3) will not. If hashes must match another system, write a cross-system fixture test with literal expected hex values and pin them.

Package is frozen (last release 2021)

It is stable, tiny, and MIT, but unmaintained — no Laravel 12 tags to wait on because there is nothing to adapt. If you need patches, fork it or vendor the single class. Do not build a critical contract on it without a snapshot test.

Pin the algorithm with tests

Seeds and method changes are silent behavior changes. Assert literal outputs:

$this->assertSame('5e928f0f', Murmur::hash('hello', 0));

This catches accidental swaps to crc32, md5, or a different seed during refactors.

Bias and bucket math

$n % 100 from a 28-bit space has negligible bias for typical bucket counts, but avoid tiny ranges (e.g. % 3) if statistical fairness is audited — use the full hash or a larger modulus, then scale.

Extension points

There are none in the package itself — no interfaces, no config, no container hooks. Your extension point is your own wrapper service: put the seed in config, add a sample(string $key, float $rate): bool helper, and keep the static class behind that boundary so you can swap implementations later without touching call sites.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings