visus/cuid2
PHP CUID2 generator for collision-resistant, secure, URL-safe IDs built for distributed systems. Uses SHA3-512 with time and entropy for scalable uniqueness. Supports instance or static usage plus identifier validation; GMP can improve performance.
Installation:
composer require visus/cuid2
Verify PHP 8.2+ and SHA3-512 support (php -r "echo hash_algos();").
First Use Case: Generate a default-length (24-char) CUID in a controller:
use Visus\Cuid2\Cuid2;
$cuid = Cuid2::generate(); // e.g., "a1b2c3d4e5f6g7h8i9j0k1l2"
return response()->json(['id' => $cuid]);
Where to Look First:
src/Cuid2.php for core generation logicsrc/Utils.php for base conversion internalstests/Cuid2Test.php for validation patterns// In User.php
class User {
protected string $id;
public function __construct() {
$this->id = Cuid2::generate(); // Auto-generate on creation
}
public static function fromArray(array $data): self {
$user = new self();
$user->id = $data['id'] ?? Cuid2::generate(); // Fallback to CUID2
return $user;
}
}
// Migration
Schema::create('users', function (Blueprint $table) {
$table->cuid2('id')->primary(); // Custom column type
$table->string('name');
$table->timestamps();
});
// Model
class User extends Model {
protected $keyType = 'string';
public $incrementing = false;
}
use Illuminate\Validation\Rule;
$validator = Validator::make($request->all(), [
'external_id' => [
'required',
Rule::custom(function ($attribute, $value, $fail) {
if (!Cuid2::isValid($value, 24)) {
$fail('Invalid CUID2 format.');
}
}),
],
]);
// Generate 1000 CUIDs in bulk (e.g., for testing)
$batch = array_map(fn() => Cuid2::generate(10), range(1, 1000));
Service Container Binding:
$app->bind(Cuid2::class, function () {
return new Cuid2(24); // Default length
});
Custom Length Strategy:
// config/cuid2.php
return [
'default_length' => env('CUID_LENGTH', 24),
'short_length' => 10,
];
// Usage
$shortId = Cuid2::generate(config('cuid2.short_length'));
Fingerprint Customization (Advanced):
Override Fingerprint singleton for environment-specific identifiers:
$fingerprint = new \Visus\Cuid2\Fingerprint(
env('APP_ENV'), // Custom environment
env('HOSTNAME_OVERRIDE') // Force specific hostname
);
Performance Optimization:
Cuid2 objects in service providers:
$app->singleton(Cuid2::class, fn() => new Cuid2());
GMP Dependency:
ext-gmp, generation is 300x slower (fallback to pure PHP).php -m | grep gmp or phpinfo() for missing extension.Counter Collisions:
Counter::INCREMENT or use a distributed counter (e.g., Redis).Validation False Positives:
Cuid2::isValid() only checks format, not uniqueness.if (!Cuid2::isValid($id) || User::where('id', $id)->exists()) {
throw new \InvalidArgumentException('Invalid or duplicate ID');
}
Length Constraints:
\InvalidArgumentException for out-of-range lengths.Fingerprint Stability:
gethostname() or getmypid() behavior (e.g., Docker) may alter fingerprints.env('HOSTNAME') for consistent environments.Inspect Components:
$cuid = new Cuid2(24);
echo $cuid->getPrefix(); // First character (e.g., 'a')
echo $cuid->getTimestamp(); // Unix timestamp in ms
echo $cuid->getFingerprint(); // Binary hash of machine/process
Benchmarking:
$start = microtime(true);
for ($i = 0; $i < 1000; $i++) {
Cuid2::generate();
}
echo "Time: " . (microtime(true) - $start) . "s";
Collision Testing:
$ids = [];
$collision = false;
for ($i = 0; $i < 10000; $i++) {
$id = Cuid2::generate(10);
if (in_array($id, $ids)) {
$collision = true;
break;
}
$ids[] = $id;
}
echo $collision ? "Collision detected!" : "No collisions in 10k IDs.";
Custom Hashing:
Override Utils::base36Encode() for alternative encoding:
class CustomCuid2 extends Cuid2 {
protected function encodeHash(string $hash): string {
return base64_encode($hash); // Example: Base64 instead of Base36
}
}
Environment-Specific Fingerprints:
$fingerprint = new \Visus\Cuid2\Fingerprint(
env('APP_ENV'), // e.g., 'staging'
env('SERVICE_NAME') // e.g., 'user-service'
);
Counter Reset: For testing, reset the counter singleton:
\Visus\Cuid2\Counter::reset();
Length Validation:
Extend Cuid2 to enforce custom length rules:
class StrictCuid2 extends Cuid2 {
public function __construct(int $length = 24) {
if ($length !== 24) {
throw new \InvalidArgumentException('Only 24-char CUIDs allowed');
}
parent::__construct($length);
}
}
SHA3-512 Requirement:
\RuntimeException if sha3-512 is unavailable.Randomness Sources:
random_bytes() (PHP 7.0+) or openssl_random_pseudo_bytes().random_bytes:
$mock = $this->createMock(Random\Randomizer::class);
$mock->method('getBytes')->willReturn('test');
$cuid = new Cuid2(10, $mock);
Windows Hostname Limits:
gethostname() on Windows returns max 15 chars.env('COMPUTERNAME') for consistent lengths.Time Precision:
microtime(true) for timestamps (millisecond precision).hrtime().How can I help you explore Laravel packages today?