Download the PHP package erikwang2013/snowflake-php without Composer
On this page you can find all versions of the php package erikwang2013/snowflake-php. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download erikwang2013/snowflake-php
More information about erikwang2013/snowflake-php
Files in erikwang2013/snowflake-php
Package snowflake-php
Short Description A distributed unique ID generator based on Twitter's Snowflake algorithm, compatible with Laravel, Yii2, Yii3, Webman, ThinkPHP, and Hyperf.
License MIT
Homepage https://package.erik.xyz/snowflake/
Informations about the package snowflake-php
Snowflake PHP
The mascot ships with the code too — echo Snowflake::MASCOT; prints it in any terminal.
A distributed unique ID generator based on Twitter's Snowflake algorithm, compatible with Laravel, Yii2, Yii3, Webman, ThinkPHP, and Hyperf.
日本語
About
Snowflake PHP generates 64-bit, k-ordered, globally unique IDs without requiring a central coordinator. Each ID is composed of a timestamp, datacenter ID, worker ID, and sequence number — allowing well over a million IDs per second per node with no database round-trips.
Key features:
- Pure PHP, zero dependencies — no extensions or external services required
- Pluggable sequence resolvers — sequential, random and Redis-backed strategies ship built in, or bring your own
- Flexible bit allocation — adjust timestamp/worker/datacenter/sequence bits to fit your scale
- Clock drift tolerance — configurable tolerance window for NTP adjustments
- Framework agnostic — first-class adapters for Laravel, Yii2, Yii3, ThinkPHP, Webman, and Hyperf, or plain PHP with no container at all
- ID parsing — decompose generated IDs back into timestamp, node, and sequence components
Project Structure
Architecture
Four layers, with dependencies pointing in one direction only:
- Application layer — your Laravel / Yii2 / Yii3 / Webman / ThinkPHP / Hyperf application, any PSR-11 container, or plain PHP; it only ever asks for a
Snowflakeinstance. - Adapter layer — one adapter per framework, plus a container-agnostic PSR-11 factory. Each registers a single shared instance and ships a publishable config file.
- Core layer —
Snowflakeis the only stateful class: it validates the configuration, precomputes the bit shifts and fixed node bits, generates IDs, and parses them back. - Contracts & resolvers —
SequenceResolveris the extension point. The core delegates every sequence allocation to it, so sequence strategy can be swapped without touching the generator. - Cross-cutting — a semantic exception hierarchy plus a single commented configuration file shared by every adapter.
Feature Design
Features group into three domains: core (generation, bit allocation, parsing), extension (pluggable resolvers, clock-drift handling, framework adapters), and engineering (strict config validation, semantic exceptions, tests and release automation).
ID Lifecycle
Every id() call walks the same path:
- Read the clock and check for backward drift — tolerated up to
clock_tolerance_ms; beyond thatclock_drift_strategydecides whether to wait for the clock to catch up ('wait') or refuse to generate ('throw'). - Convert to an epoch offset and reject offsets that are negative or past the timestamp limit.
- Ask the sequence resolver for the next slot in this millisecond; when all 4096 slots are used, spin to the next millisecond and retry once.
- Assemble
(offset << timestampShift) | fixedBits | sequence, advancelastTimestamp, and return the ID.
Instance state (lastTimestamp plus the resolver cursor) lives in memory and is never shared across processes or coroutines.
Requirements
- PHP >= 8.0 (8.0 – 8.5 tested in CI, alongside PHPStan level 8 on
src/) - 64-bit system (required for native 64-bit integer operations)
- One instance per process/coroutine — a Snowflake instance keeps its sequence state in memory and must not be shared across processes or coroutines
Installation
Quick Start
With custom worker and datacenter IDs:
Configuration Reference
| Key | Type | Default | Description |
|---|---|---|---|
epoch |
int | 1704067200000 |
Custom epoch in ms (default: 2024-01-01 UTC) |
worker_id |
int | 0 |
Worker/node identifier |
datacenter_id |
int | 0 |
Datacenter identifier |
worker_bits |
int | 5 |
Bits for worker ID |
datacenter_bits |
int | 5 |
Bits for datacenter ID |
sequence_bits |
int | 12 |
Bits for sequence number |
sequence_resolver |
string | SequentialSequenceResolver |
FQCN of SequenceResolver |
clock_tolerance_ms |
int | 0 |
Max backward clock drift (0 = strict) |
clock_drift_strategy |
string | 'throw' |
'throw' refuses to generate when the clock moves backwards beyond the tolerance; 'wait' spins until the wall clock catches up, giving up after clock_drift_wait_ms and then throwing ClockDriftException |
clock_drift_wait_ms |
int | 1000 |
How long the 'wait' strategy waits before giving up |
Bit Layout
Default layout (63 data bits + 1 sign bit = 64 bits total):
Maximum lifespan with default epoch: ~69 years (until ~2093).
Every bit handed to the node id or the sequence is taken from the timestamp, so a wide sequence silently shortens the generator's life:
| worker + datacenter + sequence bits | timestamp bits | usable lifespan |
|---|---|---|
| 5 + 5 + 12 (default) | 41 | ~69.7 years |
| 7 + 7 + 10 | 39 | ~17.4 years |
| 5 + 5 + 16 | 37 | ~4.4 years |
| 5 + 5 + 20 | 33 | ~99 days |
Ask for the limit of any layout:
Snowflake::lifespanMs(int $workerBits = 5, int $datacenterBits = 5, int $sequenceBits = 12): int returns the maximum timestamp offset in milliseconds for a layout; the arguments default to the default layout. Once the offset reaches that limit the epoch is exhausted — a stale epoch whose window has already closed makes the very first id() call throw TimestampOverflowException.
Using Configuration Array
Framework Integration
Laravel
The package supports Laravel auto-discovery. After installation:
-
Publish the config (optional):
-
Configure environment variables in
.env: - Use the Facade or dependency injection:
Webman
-
Copy the plugin config to your project:
-
Register a singleton in
process.phpor bootstrap: - Usage:
ThinkPHP 6+
-
Copy the config file to your project:
-
Register the service in
app/service.php: - Usage:
Hyperf
-
Publish the config:
-
Register the DI binding in
config/autoload/dependencies.php: - Usage via constructor injection:
Yii2
-
Copy the config file to your application:
-
Register the generator as a container singleton in
config/web.php(andconfig/console.phpwhen the console generates IDs too): - Usage:
The copied config reads the same SNOWFLAKE_* environment variables as the
other adapters. The container config section needs Yii 2.0.11+.
Yii3
Yii3 wires itself up: composer.json declares the package config in
extra.config-plugin, so the yiisoft/config plugin merges
src/Adapters/Yii3/config/params.php and di.php into the application's
params and di groups on install. No registration step.
-
Optionally override the defaults in your application's
config/common/params.php— the keys you list win over the package's, anything you leave out falls back to the generator's built-in defaults: - Use the container or constructor injection:
params.php lists every available key with its default.
PSR-11 containers
Symfony, Slim, Laminas and any other container: register the factory. It depends on nothing, so any container works — psr/container is not required:
SnowflakeFactory::fromEnvironment() reads the same SNOWFLAKE_* variables the Laravel adapter uses. A PSR-11 container calls the factory object itself, so a Symfony service definition is one line:
Native PHP (no framework)
Nothing in this package needs a framework — the adapters above only wire
Snowflake into a container for you. Without one, build it yourself:
A runnable version of this — including a framework-free lazy singleton and the
invariants it checks — lives in docs/examples/plain-php.php:
Choosing a lifetime
The instance keeps lastTimestamp and the sequence cursor in memory, so how
long it lives is the one thing to get right:
| Runtime | Build the instance |
|---|---|
| PHP-FPM, mod_php, CLI | Inline, per request or command — nothing is shared between them. |
| Swoole, ReactPHP, RoadRunner, FrankenPHP | Once per worker process, from the worker-start callback, with a unique (datacenter_id, worker_id) pair. |
Never share one instance between coroutines or threads: id() reads and writes
its own state, so two concurrent calls can interleave and hand out the same
sequence number. Create one instance per coroutine, or guard the shared one with
a mutex.
ID Parsing
Decompose a Snowflake ID into its components:
The datetime member is formatted with PHP's date() in the server's default timezone, so two hosts in different timezones render the same ID differently. timestamp_ms is the timezone-independent absolute value — compare that one when reconciling IDs across machines.
Sequence Resolvers
Three built-in implementations:
SequentialSequenceResolver (default)
Classic Snowflake behavior. Sequence starts at 0 each millisecond and increments sequentially. Guarantees monotonically increasing IDs within a single node.
RandomSequenceResolver
Starts each millisecond at a random sequence number, then increments. Less predictable than sequential IDs while keeping IDs within a millisecond monotonic.
Custom Resolver
Implement Erikwang2013\Snowflake\Contracts\SequenceResolver:
RedisSequenceResolver
The in-process resolvers keep the sequence in memory, so processes sharing a node id can hand out the same sequence number. RedisSequenceResolver keeps the counter in Redis instead — the one to use when several processes share a (datacenter_id, worker_id) pair:
__construct(object $client, string $keyPrefix = 'snowflake:seq:', int $ttlSeconds = 1) — the client is injected, so neither the redis extension nor Predis is required. A long TTL is safe: the counter then keeps growing inside the same millisecond, which correctly yields null until the next millisecond begins.
Exception Handling
| Exception | When |
|---|---|
InvalidWorkerIdException |
Worker ID exceeds 2^worker_bits - 1 |
InvalidDatacenterIdException |
Datacenter ID exceeds 2^datacenter_bits - 1 |
ClockDriftException |
System clock moved backwards beyond tolerance |
TimestampOverflowException |
Epoch has been exhausted (lifespan ended) |
SnowflakeException |
Base exception for all package exceptions |
Distributed Deployment
When running across multiple servers or processes, ensure each instance uses a unique (datacenter_id, worker_id) pair:
With the default 5+5 bit layout, you can support up to 32 datacenters × 32 workers = 1024 unique nodes.
To support more workers, adjust bit allocation:
Performance
IDs are generated entirely in-process with no external dependencies, so throughput is bounded by PHP's own microtime() call plus a handful of integer operations.
Measured on one core of a developer machine (PHP 8.3.7, Xdebug disabled, 300k iterations, best of 5):
| Operation | Throughput | Per call |
|---|---|---|
microtime(true) alone — the floor |
10.3M/s | 97 ns |
id() — default 5+5+12 layout |
1.6M/s | 633 ns |
id() + parseId() |
282k/s | 3.5 µs |
Snowflake::fromConfig() |
167k/s | 6.0 µs |
Generation costs about six times a bare clock call, and a node's sequence ceiling (4096 IDs/ms = 4.1M/s) stays well above what one PHP process can consume. Parsing and construction are diagnostic operations, not hot paths — build the instance once per process and keep parseId() out of tight loops.
Reproduce it on your own machine:
It prints ops/sec and ns/op against a bare microtime() baseline, best-of-N with the spread. Two things decide whether the absolute numbers mean anything: Xdebug (it can cost an order of magnitude — the header reports when it is loaded) and a busy or virtualised host, whose own clock call can dominate the measurement. Compare against the baseline rather than reading any single number as a promise.
Support Welcome
| WeChat Pay | Alipay |
|---|---|
If this project helps you, feel free to show your support~
License
MIT — Copyright (c) 2026 erik https://erik.xyz