glhd/bits
Generate unique 64-bit IDs in PHP for distributed systems. Create Twitter Snowflake, Sonyflake, or custom bit-sequence identifiers. Configure worker/datacenter IDs and a custom epoch to avoid collisions across servers.
Install the package:
composer require glhd/bits
Configure environment variables (critical for distributed systems):
BITS_WORKER_ID=1 # Unique per worker (0-31)
BITS_DATACENTER_ID=1 # Unique per datacenter (0-31)
BITS_EPOCH=2023-01-01 # Default; adjust if testing with past dates
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)
snowflake_id() (shorthand) or sonyflake().HasSnowflakes trait for auto-generated IDs.$snowflake->toCarbon() for time-based queries.use Glhd\Bits\Database\HasSnowflakes;
class User extends Model
{
use HasSnowflakes; // Auto-generates Snowflake on create
protected $casts = [
'legacy_id' => Snowflake::class, // Casts DB int to Snowflake object
];
}
Replace created_at comparisons with Snowflake IDs:
// Instead of:
User::where('created_at', '>', now()->subDays(7));
// Use:
$userIdThreshold = app(\Glhd\Bits\Snowflake::class)
->firstForTimestamp(now()->subDays(7))
->id();
User::where('id', '>', $userIdThreshold);
Register in AppServiceProvider:
use Glhd\Bits\Support\Livewire\SnowflakeSynth;
public function boot(): void
{
Livewire::propertySynthesizer(SnowflakeSynth::class);
}
Now use in components:
public $snowflakeId;
public function mount()
{
$this->snowflakeId = snowflake_id(); // Synthesized as Snowflake object
}
Extend Bits for non-Snowflake/Sonyflake formats:
use Glhd\Bits\Bits;
class CustomBits extends Bits
{
protected static function getBitAllocation(): array
{
return [
'timestamp' => 35,
'worker' => 10,
'sequence' => 19,
];
}
}
BIGINT columns (signed/unsigned depends on your bit allocation).Snowflake::setTestNow() instead of Carbon::setTestNow():
Snowflake::setTestNow(now()->subHours(1));
Number precision issues:
$snowflake->toString(); // "65898467809951744"
Worker/Datacenter Limits:
BITS_WORKER_ID and BITS_DATACENTER_ID environment variables. For Lambda/Vapor, implement a locking mechanism (e.g., Redis) to avoid collisions.Epoch Misconfiguration:
BITS_EPOCH to a future date throws an exception.if (now()->lt(config('bits.epoch'))) {
throw new \RuntimeException('Epoch must be in the past.');
}
Sequence Collisions:
$resolver = new \Glhd\Bits\CacheSequenceResolver(
app('cache.store'),
'bits:sequence'
);
Livewire Serialization:
Bits classes may not serialize correctly.BitsSynth instead of SnowflakeSynth for non-standard formats.Validate IDs:
$snowflake = Snowflake::fromId($idFromDb);
if (!$snowflake) {
throw new \InvalidArgumentException('Invalid Snowflake ID.');
}
Inspect Bit Structure:
$snowflake = Snowflake::make();
dd([
'timestamp' => $snowflake->timestamp,
'datacenter' => $snowflake->datacenter_id,
'worker' => $snowflake->worker_id,
'sequence' => $snowflake->sequence,
]);
Check for Time Skew:
$now = now();
$snowflakeNow = Snowflake::make()->toCarbon();
if ($now->diffInSeconds($snowflakeNow) > 1) {
// Clock skew detected!
}
Custom Resolvers:
Override SequenceResolver for custom backends (e.g., database):
class DatabaseSequenceResolver implements SequenceResolver
{
public function next(): int { /* ... */ }
public function reset(): void { /* ... */ }
}
Blueprint Macros: Extend Laravel’s query builder:
use Glhd\Bits\Support\BlueprintMacros;
BlueprintMacros::register();
// Now use `whereSnowflakeAfter($timestamp)` in queries.
Event Dispatching: Hook into ID generation:
Snowflake::created(function (Snowflake $snowflake) {
event(new SnowflakeGenerated($snowflake));
});
pcntl or pthreads are disabled if using BITS_WORKER_ID in multi-process setups.Migration Compatibility: Use Schema::bigIncrements() for new tables (avoids signed/unsigned conflicts).
Model Events: Listen for retrieved/saved to log Snowflake metadata:
User::saved(function (User $user) {
logger()->info('Generated Snowflake', [
'id' => $user->id,
'timestamp' => $user->id->toCarbon(),
]);
});
Testing:
// Reset test time globally
Snowflake::setTestNow(now()->subDay());
How can I help you explore Laravel packages today?