UUIDs are 128-bit numbers used to identify items uniquely. You've probably seen or used UUIDs in your applications before. They are usually represented as an hexadecimal value with the format 8-4-4-4-12 (e.g. 6ba7b814-9dad-11d1-80b4-00c04fd430c8
).
Most developers use random UUIDs, which don't contain any information about where or when they were generated. These are technically called UUIDv4 and they are one of the eight different types of UUIDs available.
Symfony provides the UID component to generate UUIDs:
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV4
// and its value is a random UUID
$uuid = Uuid::v4();
In this article, we'll focus on UUIDv5, which generates UUIDs based on a name and a namespace. Before diving into it, let's explain the problem they solve.
Imagine that you work on an e-commerce application and need to include the product ID in some URLs (e.g. /show/{productId}/{productSlug}
). The product IDs are unique within your application, but it's internal information and you are not comfortable sharing it publicly.
If the product IDs are numbers, then you can use tools like Sqids (formerly known as Hashids) to generate deterministic (and reversible) unique IDs from a given number(s). However, this problem can also be solved with UUIDs.
UUIDv5 generate UUIDs whose contents are based on a given name
(any arbitrary string) and a namespace
. The namespace
is used to ensure that all the names that belong to it are unique within that namespace. The spec defines a few standard namespaces (for generating UUIDs based on URLs, DNS entries, etc.) but you can use any other UUID (e.g. a random UUID) as the namespace:
use Symfony\Component\Uid\Uuid;
// ...
final readonly class ProductHandler
{
// this value was generated randomly using Uuid::v4()
private const string UUID_NAMESPACE = '8be5ecb9-1eba-4927-b4d9-73eaa98f8b65';
// ...
public function setUuid(Product $product): void
{
$namespace = Uuid::fromString(self::UUID_NAMESPACE);
$product->setUuid(Uuid::v5($namespace, $this->getProductId());
// e.g. if the productId is 'acme-1234',
// the generated UUID is 'eacc432f-a7c1-5750-8f9f-9d69cb287987'
}
}
UUIDv5 roughly performs sha1($namespace.$name)
when generating values. This way, the generated UUIDs are not reversible (or guessable by external actors) and are deterministic (they always generate the same UUID for a given string).
If you want to make the public IDs shorter, use any of the methods provided by Symfony to convert UUIDs:
$namespaceAsString = '8be5ecb9-1eba-4927-b4d9-73eaa98f8b65';
$namespace = Uuid::fromString($namespaceAsString);
$name = 'acme-1234';
$uuid = Uuid::v5($namespace, $name);
// (string) $uuid = 'eacc432f-a7c1-5750-8f9f-9d69cb287987'
$shortUuid = $uuid->toBase58();
// $shortUuid = 'VzeJE1ydqWXpJJMwnavj3t'
The UUID spec defines many other types of UUIDs which fit different scenarios. There's even a UUIDv3 which is the same as UUIDv5 but uses md5
hashes instead of sha1
. That's why UUIDv5 is preferred over UUIDv3.
Check out the Symfony docs about the different types of UUIDs to know more about them.