Generating UUIDs in PHP
On this page
PHP has no built-in UUID functions in the core language. The community uses one of two libraries: ramsey/uuid (the de facto standard) or symfony/uid (newer, leaner, part of Symfony 5.2+).
Quick start with ramsey/uuid
composer require ramsey/uuid
use Ramsey\Uuid\Uuid;
$id = Uuid::uuid4(); // v4 random
$v7 = Uuid::uuid7(); // time-ordered (4.7+)
$v5 = Uuid::uuid5(Uuid::NAMESPACE_URL, 'https://example.com/users/42');
echo $id->toString(); // canonical
echo (string) $id; // same — implements Stringable
echo $id->getHex(); // no hyphens
echo bin2hex($id->getBytes()); // 32-char hex from 16 bytes
UuidInterface is immutable, comparable, and serializable. Cast to string anywhere a string is expected.
Parsing
use Ramsey\Uuid\Uuid;
$id = Uuid::fromString('0e6f1b8c-2c33-4f1f-9c0b-2a3d4e5f6a7b');
$id = Uuid::fromBytes($binary);
$valid = Uuid::isValid($input);
fromString throws InvalidUuidStringException on invalid input. Use Uuid::isValid() first if you’re not sure.
Symfony alternative: symfony/uid
composer require symfony/uid
use Symfony\Component\Uid\Uuid;
use Symfony\Component\Uid\UuidV4;
use Symfony\Component\Uid\UuidV7;
$v4 = Uuid::v4();
$v7 = Uuid::v7();
$v5 = Uuid::v5(Uuid::NAMESPACE_URL, 'https://example.com');
echo (string) $v7;
echo $v7->toBase32(); // Crockford base32 (ULID-style encoding)
echo $v7->toBase58(); // shorter still
symfony/uid has a smaller surface area and is well-suited if you’re already on the Symfony stack. ramsey/uuid is more battle-tested and has more features (RFC variant detection, custom namespaces, time-precision controls).
Laravel
Laravel ships with first-class UUID support via traits:
use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use HasUuids;
}
HasUuids (Laravel 9.30+) makes id a UUID column and generates v4 on insert. For v7, add newUniqueId:
class User extends Model
{
use HasUuids;
public function newUniqueId(): string
{
return (string) \Ramsey\Uuid\Uuid::uuid7();
}
}
Migration:
Schema::create('users', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('email')->unique();
$table->timestamps();
});
Symfony / Doctrine ORM
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
#[ORM\Entity]
class User
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
private Uuid $id;
public function __construct()
{
$this->id = Uuid::v7();
}
}
For ramsey/uuid + Doctrine, use the ramsey/uuid-doctrine adapter.
Database storage
For PostgreSQL: use uuid (native), 16 bytes.
For MySQL 8+: use BINARY(16) and convert with UUID_TO_BIN / BIN_TO_UUID. Both ramsey/uuid and symfony/uid have helpers for this:
$bytes = $id->getBytes(); // 16 raw bytes — ready for BINARY(16)
$id = Uuid::fromBytes($bytes); // round-trip
Don’t store as VARCHAR(36) — see SQL UUID.
JSON serialization
$id = Uuid::uuid7();
echo json_encode(['id' => $id]);
// {"id":"01928a47-3b30-7c5e-9d1a-f0b8c4a7e923"}
Both libraries implement JsonSerializable. The default form is canonical hyphenated.
Validation in HTTP routes
Laravel:
Route::get('/users/{id}', function (string $id) {
abort_if(!\Ramsey\Uuid\Uuid::isValid($id), 400);
// ...
})->where('id', '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}');
The where constraint validates at the routing layer — invalid UUIDs return 404 before your handler runs.
Symfony:
#[Route('/users/{id}', requirements: ['id' => Requirement::UUID])]
public function show(Uuid $id): Response
{
// $id is already a Uuid instance, validated
}
Performance
ramsey/uuid is sufficient for typical web throughput. If UUID generation shows up in a profile (rare), symfony/uid is somewhat faster, and Rust extensions like php-ext-ramsey-uuid can give a 10× boost — usually not worth the dependency.
Common pitfalls
Uuid::uuid4()vs\Ramsey\Uuid\Uuid::uuid4()— namespace imports matter. The class isRamsey\Uuid\Uuid, not the olderRhumsaa\Uuid\Uuid.- Don’t compare with
==. Use$a->equals($b)or compare strings —==on UUID objects checks reference equality, which is almost never what you want. getBytes()returns raw 16 bytes — printing it produces gibberish. Usebin2hex($id->getBytes())to see it.- MySQL
BINARY(16)storage requires conversion — bind viagetBytes(), read back viaUuid::fromBytes(). Frameworks usually handle this for you, but raw PDO does not.
Cheat sheet
| Goal | Code (ramsey/uuid) |
|---|---|
| Random | Uuid::uuid4() |
| Time-ordered | Uuid::uuid7() |
| Deterministic | Uuid::uuid5(ns, name) |
| Parse | Uuid::fromString($s) |
| Validate | Uuid::isValid($s) |
| Bytes for DB | $id->getBytes() |
Try the tools
- UUID generator — instant in-browser
- UUID v7 generator — modern primary keys
- UUID validator — paste, decode
- SQL UUID guide — cross-database storage patterns