glhd/bits
Generate unique 64‑bit IDs for distributed systems in PHP. Create Twitter Snowflake, Sonyflake, or custom bit-sequence identifiers with configurable worker/datacenter IDs and epoch for compact, time-ordered, collision-resistant IDs across multiple servers.
Installation:
composer require glhd/bits
Configure Environment:
Add to .env:
BITS_WORKER_ID=1
BITS_DATACENTER_ID=1
(Replace values with unique IDs for your worker/datacenter. Ensure no duplicates across servers.)
First Use Case: Generate a Snowflake ID in a controller or service:
use Glhd\Bits\Snowflake;
$id = Snowflake::make()->id(); // Returns int (e.g., 65898467809951744)
Or use the helper:
$id = snowflake_id(); // Same result
Verify Configuration: Check the generated ID’s metadata:
$snowflake = Snowflake::make();
echo $snowflake->timestamp; // Unix timestamp (relative to BITS_EPOCH)
echo $snowflake->worker_id; // Your configured worker ID
toCarbon(), is(), and id().Use HasSnowflakes to auto-generate IDs:
use Glhd\Bits\Database\HasSnowflakes;
class Order extends Model
{
use HasSnowflakes; // Auto-generates Snowflake on create
}
id column to be bigint unsigned.Custom Casts:
protected $casts = [
'external_id' => Snowflake::class, // Casts DB int to Snowflake object
];
Replace created_at filters with Snowflake IDs:
// Instead of:
User::where('created_at', '>', now()->subDays(7));
// Use:
$minId = app(\Glhd\Bits\Snowflake::class)->firstForTimestamp(now()->subDays(7));
User::where('id', '>', $minId);
created_at; leverages primary key.BITS_EPOCH is before your earliest data.Multi-server Setup:
BITS_DATACENTER_ID (0–31) per region.BITS_WORKER_ID (0–31) per server in the same datacenter.1 (US-East), Worker 5 (App Server 1).Lambda/Vapor Workaround: Use a centralized ID service (e.g., Redis) or implement locking to avoid collisions:
$lock = app(\Illuminate\Cache\Lock::class)->get('bits-lock');
if ($lock->get()) {
$id = snowflake_id();
}
Enable client-side ID generation:
// AppServiceProvider.php
Livewire::propertySynthesizer(\Glhd\Bits\Support\Livewire\SnowflakeSynth::class);
HasSnowflakes for DB storage).Extend Bits for Sonyflake or custom layouts:
use Glhd\Bits\Bits;
$sonyflake = Bits::sonyflake()->make();
bigint unsigned for Snowflake columns (64-bit range: 0 to 18,446,744,073,709,551,615).return response()->json(['id' => (string) $snowflake]);
Number.MAX_SAFE_INTEGER is 2^53 - 1 (~9e15). Snowflakes exceed this.Bits::setTestNow() instead of Carbon::setTestNow():
Bits::setTestNow(now()->subDays(5)); // Overrides Snowflake timestamps
CacheSequenceResolver:
$bits = Bits::snowflake()->withSequenceResolver(
new \Glhd\Bits\CacheSequenceResolver(cache())
);
Worker/Datacenter ID Collisions
InvalidArgumentException ("Worker ID must be unique").BITS_WORKER_ID or BITS_DATACENTER_ID.php artisan tinker to verify:
echo Snowflake::make()->worker_id; // Should match .env
Epoch Mismatches
BITS_EPOCH set to a date after your first ID was generated.BITS_EPOCH to a date before your earliest expected ID (e.g., 2020-01-01).Lambda/Vapor Concurrency
InvalidArgumentException in serverless environments.$lock = app(\Illuminate\Cache\Lock::class)->get('bits-lock-' . request()->ip());
if ($lock->get()) {
$id = snowflake_id();
}
Livewire ID Uniqueness
HasSnowflakes for DB storage and validate uniqueness:
$this->validate(['id' => 'required|unique:orders,id']);
JavaScript ID Truncation
9e+18 in JavaScript.// PHP
return (string) $snowflake;
// JavaScript
const id = '65898467809951744'; // String, not number
Inspect ID Components
$snowflake = Snowflake::make();
dd([
'id' => $snowflake->id,
'timestamp' => $snowflake->toCarbon(),
'worker' => $snowflake->worker_id,
'datacenter' => $snowflake->datacenter_id,
'sequence' => $snowflake->sequence,
]);
Validate Epoch
php artisan tinker
>>> Bits::snowflake()->epoch->format('Y-m-d')
// Should match your .env BITS_EPOCH
Check for Future IDs
$now = now();
$snowflake = Snowflake::make();
if ($s
How can I help you explore Laravel packages today?