erikwang2013 / snowflake-php
A distributed unique ID generator based on Twitter's Snowflake algorithm, compatible with Laravel, Webman, ThinkPHP, and Hyperf.
Requires
- php: >=8.0
Requires (Dev)
- phpunit/phpunit: ^9.5 || ^10.0 || ^11.0
Suggests
- hyperf/framework: For Hyperf integration
- laravel/framework: For Laravel integration
- topthink/framework: For ThinkPHP 6+ integration
- workerman/webman-framework: For Webman integration
README
A distributed unique ID generator based on Twitter's Snowflake algorithm, compatible with Laravel, Webman, ThinkPHP, and Hyperf.
中文文档请参阅 README.zh-CN.md
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 tens of thousands of 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 — built-in sequential and random strategies, 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 with first-class adapters for Laravel, ThinkPHP, Webman, and Hyperf
- ID parsing — decompose generated IDs back into timestamp, node, and sequence components
Requirements
- PHP >= 8.0
- 64-bit system (required for native 64-bit integer operations)
Installation
composer require erikwang2013/snowflake-php
Quick Start
use Snowflake\Snowflake; $snowflake = new Snowflake(); $id = $snowflake->id(); // e.g. 508047278033704960 $id = $snowflake->nextId(); // alias for id()
With custom worker and datacenter IDs:
$snowflake = new Snowflake(workerId: 5, datacenterId: 3); $id = $snowflake->id();
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) |
Bit Layout
Default layout (63 data bits + 1 sign bit = 64 bits total):
| reserved(1) | timestamp(41) | datacenter(5) | worker(5) | sequence(12) |
Maximum lifespan with default epoch: ~69 years (until ~2093).
Using Configuration Array
$snowflake = Snowflake::fromConfig([ 'worker_id' => 1, 'datacenter_id' => 2, 'epoch' => 1704067200000, ]); $id = $snowflake->id();
Framework Integration
Laravel
The package supports Laravel auto-discovery. After installation:
- Publish the config (optional):
php artisan vendor:publish --tag=snowflake-config
- Configure environment variables in
.env:
SNOWFLAKE_WORKER_ID=1 SNOWFLAKE_DATACENTER_ID=1
- Use the Facade or dependency injection:
// Facade use Snowflake; $id = Snowflake::id(); // Dependency injection use Snowflake\Snowflake; class OrderController { public function store(Snowflake $snowflake) { $orderId = $snowflake->id(); } } // Container access $id = app('snowflake')->id(); $id = app(Snowflake::class)->id();
Webman
- Copy the plugin config to your project:
cp vendor/erikwang2013/snowflake-php/src/Adapters/Webman/config/app.php \ config/plugin/erikwang2013/snowflake-php/app.php
- Register a singleton in
process.phpor bootstrap:
use Snowflake\Snowflake; Worker::$container->add(Snowflake::class, function () { return Snowflake::fromConfig( config('plugin.erikwang2013.snowflake-php.app.snowflake') ); });
- Usage:
$id = Worker::$container->get(Snowflake::class)->id();
ThinkPHP 6+
- Copy the config file to your project:
cp vendor/erikwang2013/snowflake-php/src/Adapters/ThinkPHP/config/snowflake.php \ config/snowflake.php
- Register the service in
app/service.php:
return [ \Snowflake\Adapters\ThinkPHP\Service::class, ];
- Usage:
// Container $id = app('snowflake')->id(); // Dependency injection use Snowflake\Snowflake; class IndexController { public function index(Snowflake $snowflake) { $id = $snowflake->id(); } } // Facade use Snowflake\Adapters\ThinkPHP\Facade; $id = Facade::id();
Hyperf
- Publish the config:
php bin/hyperf.php vendor:publish erikwang2013/snowflake-php
- Register the DI binding in
config/autoload/dependencies.php:
use Snowflake\Snowflake; return [ Snowflake::class => function () { return Snowflake::fromConfig(config('snowflake')); }, ];
- Usage via constructor injection:
use Snowflake\Snowflake; class OrderService { public function __construct(private Snowflake $snowflake) {} public function create(): int { return $this->snowflake->id(); } }
ID Parsing
Decompose a Snowflake ID into its components:
$id = $snowflake->id(); // Using the instance (respects custom bit layout) $parsed = $snowflake->parseId($id); // [ // 'timestamp_ms' => 1736380800123, // 'datetime' => '2025-01-09 00:00:00.123', // 'worker_id' => 5, // 'datacenter_id' => 3, // 'sequence' => 42, // ] // Static method (uses default bit layout) $parsed = Snowflake::parse($id, $epoch);
Sequence Resolvers
Two 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.
use Snowflake\Resolvers\SequentialSequenceResolver; $snowflake = new Snowflake( sequenceResolver: new SequentialSequenceResolver() );
RandomSequenceResolver
Starts each millisecond with a random sequence number. Makes IDs less predictable (prevents enumeration) but IDs within the same millisecond are not monotonic.
use Snowflake\Resolvers\RandomSequenceResolver; $snowflake = new Snowflake( sequenceResolver: new RandomSequenceResolver() );
Custom Resolver
Implement Snowflake\Contracts\SequenceResolver:
use Snowflake\Contracts\SequenceResolver; class RedisSequenceResolver implements SequenceResolver { public function next(int $timestamp, int $maxSequence): ?int { $key = "snowflake:seq:{$timestamp}"; $seq = redis()->incr($key); redis()->expire($key, 1); if ($seq > $maxSequence) { return null; } return $seq - 1; } }
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:
// Read from environment, hostname hash, or service discovery $workerId = (int) getenv('WORKER_ID'); $datacenterId = (int) getenv('DC_ID'); $snowflake = new Snowflake( workerId: $workerId, datacenterId: $datacenterId );
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:
// 10 worker bits = 1024 workers, 0 datacenter bits = single DC $snowflake = new Snowflake( workerId: $workerId, workerBits: 10, datacenterBits: 0 );
Performance
Typical throughput on modern hardware: ~500,000 IDs/second (single process).
IDs are generated purely in-process with no external dependencies. The primary bottleneck is PHP's microtime() call and integer bit operations, both of which are O(1).
License
MIT — Copyright (c) 2026 erik erik@erik.xyz — https://erik.xyz