c0dec0de/snowflake-id

A PHP library for generating Snowflake IDs

Installs: 6

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/c0dec0de/snowflake-id

v0.1.3 2025-07-31 06:06 UTC

This package is auto-updated.

Last update: 2025-10-31 00:18:28 UTC


README

A PHP library for generating Snowflake IDs

GitHub release PHP Build Tested With PHPUnit Coverage Status PHPStan Psalm Level 1 License

📦 Installation

The recommended way to install the package is via Composer:

composer require c0dec0de/snowflake-id

❄️ What is a Snowflake ID?

A Snowflake ID is a unique 64-bit integer used in distributed systems to generate sortable, collision-resistant identifiers without a central source. Originally developed by Twitter, the format has been adopted by platforms like Discord, Instagram, and Mastodon.

Each Snowflake originally consists of:

  • 41 bits for a timestamp (in milliseconds since a custom epoch)
  • 10 bits for a machine ID
  • 12 bits for a sequence number (allowing multiple IDs per millisecond) This structure ensures that IDs are unique, time-sortable, and can be generated at high throughput across distributed nodes.

This library follows the original Snowflake structure closely but splits the 10 bits originally allocated for the machine ID into two parts, using the following layout:

  • 41 bits for a timestamp (milliseconds since a custom epoch)
  • 5 bits for a datacenter ID
  • 5 bits for a worker (machine) ID
  • 12 bits for a sequence number (allowing multiple IDs per millisecond)

This split provides finer control over deployment in multi-datacenter environments.

Benefits of Snowflakes

  • Distributed-safe 64bit unique human-readable ID generation
  • Time sortable by design
  • 64-bit integer output: smaller index, faster joins compared to 128-bit alternatives such a UUIDs or ULIDs

Usage

🧙 Let's play with Snowflakes

You can start by generating your first Snowflake ID by running this code:

<?php

declare(strict_types=1);

require_once 'vendor/autoload.php';

use C0dec0de\Snowflake\Snowflake;

$snowflakeIDIntType = Snowflake::int();

var_dump($snowflakeIDIntType); // e.g., int(195302773577621504)

Or use the method that generates and returns a Snowflake ID as a string

<?php

declare(strict_types=1);

require_once 'vendor/autoload.php';

use C0dec0de\Snowflake\Snowflake;

$snowflakeIDStringType = Snowflake::string();

var_dump($snowflakeIDStringType); // e.g., string(195302773577621504)

⚠️ Best Practice

For production use, always explicitly provide the datacenterId, workerId, and store arguments when calling Snowflake::int() or Snowflake::string().

Providing these values ensures:

  • Predictable and unique ID generation
  • Safe operation across multiple machines and processes
  • Use of a production-grade store like RedisStore for concurrency safety

By default, if you omit these arguments:

  • The library will assign random values for datacenterId and workerId
  • It will fall back to InMemoryStore, which is not safe for concurrent or multi-process environments

Tip: Calling these methods without arguments is perfectly fine for prototyping, testing, or learning the API — just avoid it in real-world deployments where uniqueness and reliability are critical.

✅ Generating Globally Unique Snowflake IDs in Production

To reliably generate collision-free Snowflake IDs across multiple processes, machines, and datacenters, follow these production-grade recommendations:

  • 🔧 Assign a unique datacenterId to each datacenter.
  • 🧩 Assign a unique workerId to each machine within the same datacenter.
  • 🛡️ Use RedisStore as the store argument to ensure safe operation in concurrent and multi-process environments (e.g., high request volume, parallel workers).
  • ⏱️ Synchronize system clocks via NTP (Network Time Protocol) on all machines to avoid clock drift, which can cause inconsistencies or ID collisions.

✅ With this setup, your Snowflake ID generation is fully production-ready — scalable, reliable, and globally unique.

Setup Steps

  1. Configure your workerId and datacenterId. For example, assign a unique workerId to each machine within the same datacenterId.
  2. Instantiate RedisStore, passing in a properly configured Redis client (phpredis or predis/predis).

This approach guarantees safe, scalable ID generation across distributed environments.

<?php

declare(strict_types=1);

require_once 'vendor/autoload.php';

use C0dec0de\Snowflake\Sequence\Store\RedisStore;
use C0dec0de\Snowflake\Sequence\Store\StoreInterface;
use C0dec0de\Snowflake\Snowflake;

/**
 * Instantiate and configure your preferred Redis client (phpredis or predis).
 */
$client = new Redis();
$client->connect(
    host: 'keyval',
    port: 6379,
    timeout: 2.5,
);

/**
 * Pass the Redis client instance to RedisStore.
 */
$redisStore = new RedisStore($client);

/**
 * Generate a Snowflake ID with:
 * 
 * @param StoreInterface|null $store        - Pass the configured RedisStore instance; if null, InMemoryStore is used.
 * @param int|null            $datacenterId - Unique datacenter ID (configure per datacenter).
 * @param int|null            $workerId     - Unique worker/machine ID within the datacenter.
 */
$snowflakeIDIntType = Snowflake::int(
    store: $redisStore,
    datacenterId: 1,
    workerId: 1,
);

var_dump($snowflakeIDIntType); // e.g., int(195302773577621504)

This is the gold standard configuration for using the library to generate Snowflake IDs, guaranteeing zero collisions out of the box without any additional setup.

Customization

This library is designed for flexibility and extensibility. You can provide your own implementations of several core components.

Sequence Stores

The library includes the following built-in sequence stores:

  • InMemoryStore – ⚠️ Not safe for concurrent use. Stores sequence state in local process memory. May lead to ID collisions under high concurrency (in multi-process environments).
  • RedisStore – ✅ Production-ready. Stores sequence state in Redis. Safe for concurrent and multi-process usage. Supports both phpredis and predis clients.

To support other Redis clients or wrappers, you can implement your own adapter by extending the RedisConnectionAdapterInterface.

Both InMemoryStore and RedisStore implements the StoreInterface. You can create your own custom stores by implementing this interface.

ID Structure

The library comes with a standard Snowflake structure implementation:

  • StdStructure – Twitter-like Snowflake layout:
    • 41 bits for timestamp
    • 5 bits for datacenter ID
    • 5 bits for worker ID
    • 12 bits for sequence
    • Custom epoch: 2024-01-01 00:00:00

StdStructure implements the StructureInterface. You can create your own custom structure by implementing this interface.

Sequence Generators

Available sequence generation strategies:

  • StdSequenceGenerator – Default and recommended. Generates a sequence counter per tick, starting from 0 up to the max allowed by the structure (e.g., 4095 for 12-bit sequence capacity).
  • PidBasedSequenceGenerator – ⚠️ Unsafe and experimental. Generates sequence values based on the process ID (PID). Designed for fun and testing only. May cause collisions and should not be used in production.

All sequence generators implement the SequenceGeneratorInterface, allowing you to define and plug in your own strategy if needed.

  • SequenceGeneratorInterface – Defines the strategy for generating the sequence number within the same tick(millisecond). You can implement custom logic based on your performance, concurrency, or reset requirements.

  • StoreInterface (Sequence Store) – Responsible for storing the last sequence number and the last tick(timestamp). This allows you to plug in different storage mechanisms (e.g., in-memory, Redis, file-based) depending on your environment.

⚠️ Note:
By default (if not explicitly configured), the library uses an InMemoryStore during the ID generation process.
This store is not thread-safe and may lead to ID collisions in multi-process or concurrent environments,
as tick-sequence data remains isolated within the current process's memory and is not synchronized across processes.

For production use, consider configuring a persistent and thread-safe RedisStore.

Versioning

This library follows Semantic Versioning.

See CHANGELOG.md for release history.

Contributing

Pull requests are welcome. Please see CONTRIBUTING.md for details and code style guidelines.

License

MIT. See LICENSE file for details.

Credits

Inspired by Twitter(X)'s Snowflake algorithm.