UID 组件
UID 组件提供了用于处理唯一标识符 (UIDs) 的实用工具,例如 UUIDs 和 ULIDs。
安装
1
$ composer require symfony/uid
注意
如果在 Symfony 应用程序之外安装此组件,则必须在代码中引入 vendor/autoload.php
文件,以启用 Composer 提供的类自动加载机制。阅读本文以获取更多详细信息。
UUIDs
UUIDs (通用唯一标识符) 是软件行业中最流行的 UID 之一。 UUIDs 是 128 位数字,通常表示为五组十六进制字符:xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
(M
位是 UUID 版本,N
位是 UUID 变体)。
生成 UUIDs
使用 Uuid
类的命名构造函数或任何特定类来创建每种类型的 UUID
UUID v1 (基于时间)
使用时间戳和设备的 MAC 地址生成 UUID (阅读 UUIDv1 规范)。两者都是自动获取的,因此您无需传递任何构造函数参数
1 2 3 4
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV1
$uuid = Uuid::v1();
提示
建议使用 UUIDv7 而不是 UUIDv1,因为它提供更好的熵。
UUID v2 (DCE 安全性)
类似于 UUIDv1,但 ID 冲突的可能性非常高 (阅读 UUIDv2 规范)。它是 DCE(分布式计算环境)身份验证机制的一部分,并且 UUID 包括生成它的用户的 POSIX UID(用户/组 ID)。 Uid 组件未实现此 UUID 变体。
UUID v3 (基于名称,MD5)
从属于给定命名空间并在其中唯一的名称生成 UUID (阅读 UUIDv3 规范)。此变体对于从任意字符串生成确定性 UUID 非常有用。它的工作原理是用命名空间和名称的 md5
哈希连接填充 UUID 内容
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\Uid\Uuid;
// you can use any of the predefined namespaces...
$namespace = Uuid::fromString(Uuid::NAMESPACE_OID);
// ...or use a random namespace:
// $namespace = Uuid::v4();
// $name can be any arbitrary string
// $uuid is an instance of Symfony\Component\Uid\UuidV3
$uuid = Uuid::v3($namespace, $name);
这些是标准定义的默认命名空间
- 如果您为 DNS 条目生成 UUID,则使用
Uuid::NAMESPACE_DNS
- 如果您为 URL 生成 UUID,则使用
Uuid::NAMESPACE_URL
- 如果您为 OID(对象标识符)生成 UUID,则使用
Uuid::NAMESPACE_OID
- 如果您为 X500 DN(专有名称)生成 UUID,则使用
Uuid::NAMESPACE_X500
UUID v4 (随机)
生成随机 UUID (阅读 UUIDv4 规范)。由于其随机性,它可以确保跨分布式系统的唯一性,而无需中央协调实体。它对隐私友好,因为它不包含有关其生成地点和时间的任何信息
1 2 3 4
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV4
$uuid = Uuid::v4();
UUID v5 (基于名称,SHA-1)
它与 UUIDv3 (如上所述) 相同,但它使用 sha1
而不是 md5
来哈希给定的命名空间和名称 (阅读 UUIDv5 规范)。这使其更安全,更不容易发生哈希冲突。
UUID v6 (重新排序的基于时间)
它重新排列 UUIDv1 的基于时间的字段,使其在词法上可排序(如 ULID)。它对于数据库索引更有效 (阅读 UUIDv6 规范)
1 2 3 4
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV6
$uuid = Uuid::v6();
提示
建议使用 UUIDv7 而不是 UUIDv6,因为它提供更好的熵。
UUID v7 (UNIX 时间戳)
基于高分辨率 Unix Epoch 时间戳源(自 1970 年 1 月 1 日午夜 UTC 以来的毫秒数,不包括闰秒)生成按时间排序的 UUID (阅读 UUIDv7 规范)。建议使用此版本而不是 UUIDv1 和 UUIDv6,因为它提供更好的熵(以及更严格的 UUID 生成时间顺序)
1 2 3 4
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV7
$uuid = Uuid::v7();
UUID v8 (自定义)
为实验性或供应商特定的用例提供 RFC 兼容格式 (阅读 UUIDv8 规范)。唯一的要求是设置 UUID 的变体和版本位。 UUID 值的其余部分特定于每个实现,不应假定任何格式
1 2 3 4
use Symfony\Component\Uid\Uuid;
// $uuid is an instance of Symfony\Component\Uid\UuidV8
$uuid = Uuid::v8();
如果您的 UUID 值已以另一种格式生成,请使用以下任何方法从中创建 Uuid
对象
1 2 3 4 5 6
// all the following examples would generate the same Uuid object
$uuid = Uuid::fromString('d9e7a184-5d5b-11ea-a62a-3499710062d0');
$uuid = Uuid::fromBinary("\xd9\xe7\xa1\x84\x5d\x5b\x11\xea\xa6\x2a\x34\x99\x71\x00\x62\xd0");
$uuid = Uuid::fromBase32('6SWYGR8QAV27NACAHMK5RG0RPG');
$uuid = Uuid::fromBase58('TuetYWNHhmuSQ3xPoVLv9M');
$uuid = Uuid::fromRfc4122('d9e7a184-5d5b-11ea-a62a-3499710062d0');
您还可以使用 UuidFactory
生成 UUID。首先,您可以配置工厂的行为,使用配置文件
1 2 3 4 5 6 7 8
# config/packages/uid.yaml
framework:
uid:
default_uuid_version: 7
name_based_uuid_version: 5
name_based_uuid_namespace: 6ba7b810-9dad-11d1-80b4-00c04fd430c8
time_based_uuid_version: 7
time_based_uuid_node: 121212121212
然后,您可以将工厂注入到您的服务中,并使用它根据您定义的配置生成 UUID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
namespace App\Service;
use Symfony\Component\Uid\Factory\UuidFactory;
class FooService
{
public function __construct(
private UuidFactory $uuidFactory,
) {
}
public function generate(): void
{
// This creates a UUID of the version given in the configuration file (v7 by default)
$uuid = $this->uuidFactory->create();
$nameBasedUuid = $this->uuidFactory->nameBased(/** ... */);
$randomBasedUuid = $this->uuidFactory->randomBased();
$timestampBased = $this->uuidFactory->timeBased();
// ...
}
}
转换 UUIDs
使用这些方法将 UUID 对象转换为不同的基数
1 2 3 4 5 6 7 8
$uuid = Uuid::fromString('d9e7a184-5d5b-11ea-a62a-3499710062d0');
$uuid->toBinary(); // string(16) "\xd9\xe7\xa1\x84\x5d\x5b\x11\xea\xa6\x2a\x34\x99\x71\x00\x62\xd0"
$uuid->toBase32(); // string(26) "6SWYGR8QAV27NACAHMK5RG0RPG"
$uuid->toBase58(); // string(22) "TuetYWNHhmuSQ3xPoVLv9M"
$uuid->toRfc4122(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0"
$uuid->toHex(); // string(34) "0xd9e7a1845d5b11eaa62a3499710062d0"
$uuid->toString(); // string(36) "d9e7a184-5d5b-11ea-a62a-3499710062d0"
7.1
toString()
方法在 Symfony 7.1 中引入。
您还可以将某些 UUID 版本转换为其他版本
1 2 3 4 5 6 7 8 9 10
// convert V1 to V6 or V7
$uuid = Uuid::v1();
$uuid->toV6(); // returns a Symfony\Component\Uid\UuidV6 instance
$uuid->toV7(); // returns a Symfony\Component\Uid\UuidV7 instance
// convert V6 to V7
$uuid = Uuid::v6();
$uuid->toV7(); // returns a Symfony\Component\Uid\UuidV7 instance
使用 UUIDs
使用 Uuid
类创建的 UUID 对象可以使用以下方法(这些方法等效于 PHP 扩展的 uuid_*()
方法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
use Symfony\Component\Uid\NilUuid;
use Symfony\Component\Uid\Uuid;
// checking if the UUID is null (note that the class is called
// NilUuid instead of NullUuid to follow the UUID standard notation)
$uuid = Uuid::v4();
$uuid instanceof NilUuid; // false
// checking the type of UUID
use Symfony\Component\Uid\UuidV4;
$uuid = Uuid::v4();
$uuid instanceof UuidV4; // true
// getting the UUID datetime (it's only available in certain UUID types)
$uuid = Uuid::v1();
$uuid->getDateTime(); // returns a \DateTimeImmutable instance
// checking if a given value is valid as UUID
$isValid = Uuid::isValid($uuid); // true or false
// comparing UUIDs and checking for equality
$uuid1 = Uuid::v1();
$uuid4 = Uuid::v4();
$uuid1->equals($uuid4); // false
// this method returns:
// * int(0) if $uuid1 and $uuid4 are equal
// * int > 0 if $uuid1 is greater than $uuid4
// * int < 0 if $uuid1 is less than $uuid4
$uuid1->compare($uuid4); // e.g. int(4)
如果您正在使用不同的 UUID 格式并想要验证它们,您可以使用 isValid() 方法的 $format
参数来指定您期望的 UUID 格式
1 2 3 4
use Symfony\Component\Uid\Uuid;
$isValid = Uuid::isValid('90067ce4-f083-47d2-a0f4-c47359de0f97', Uuid::FORMAT_RFC_4122); // accept only RFC 4122 UUIDs
$isValid = Uuid::isValid('3aJ7CNpDMfXPZrCsn4Cgey', Uuid::FORMAT_BASE_32 | Uuid::FORMAT_BASE_58); // accept multiple formats
以下常量可用
Uuid::FORMAT_BINARY
Uuid::FORMAT_BASE_32
Uuid::FORMAT_BASE_58
Uuid::FORMAT_RFC_4122
Uuid::FORMAT_RFC_9562
(等效于Uuid::FORMAT_RFC_4122
)
您还可以使用 Uuid::FORMAT_ALL
常量来接受任何 UUID 格式。默认情况下,仅接受 RFC 4122 格式。
7.2
isValid() 方法的 $format
参数和相关常量在 Symfony 7.2 中引入。
在数据库中存储 UUIDs
如果您使用 Doctrine,请考虑使用 uuid
Doctrine 类型,它会自动转换为/从 UUID 对象转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Entity/Product.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
#[ORM\Column(type: UuidType::NAME)]
private Uuid $someProperty;
// ...
}
还有一个 Doctrine 生成器,可帮助为实体主键自动生成 UUID 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;
class User implements UserInterface
{
#[ORM\Id]
#[ORM\Column(type: UuidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.uuid_generator')]
private ?Uuid $id;
public function getId(): ?Uuid
{
return $this->id;
}
// ...
}
警告
出于性能原因,通常不建议将 UUID 用作主键:索引速度较慢且占用更多空间(因为二进制格式的 UUID 占用 128 位,而不是自动递增整数的 32/64 位),并且 UUID 的非顺序性质会碎片化索引。UUID v6 和 UUID v7 是唯一解决碎片问题的变体(但索引大小问题仍然存在)。
当使用内置 Doctrine 存储库方法(例如 findOneBy()
)时,Doctrine 知道如何转换这些 UUID 类型以构建 SQL 查询(例如 ->findOneBy(['user' => $user->getUuid()])
)。但是,当使用 DQL 查询或自己构建查询时,您需要将 uuid
设置为 UUID 参数的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// src/Repository/ProductRepository.php
// ...
use Doctrine\DBAL\ParameterType;
use Symfony\Bridge\Doctrine\Types\UuidType;
class ProductRepository extends ServiceEntityRepository
{
// ...
public function findUserProducts(User $user): array
{
$qb = $this->createQueryBuilder('p')
// ...
// add UuidType::NAME as the third argument to tell Doctrine that this is a UUID
->setParameter('user', $user->getUuid(), UuidType::NAME)
// alternatively, you can convert it to a value compatible with
// the type inferred by Doctrine
->setParameter('user', $user->getUuid()->toBinary(), ParameterType::BINARY)
;
// ...
}
}
ULIDs
ULIDs (通用唯一词典可排序标识符) 是 128 位数字,通常表示为 26 个字符的字符串:TTTTTTTTTTRRRRRRRRRRRRRRRR
(其中 T
表示时间戳,R
表示随机位)。
当使用 UUID 不切实际时,ULID 是 UUID 的替代方案。它们提供与 UUID 的 128 位兼容性,它们在词法上可排序,并且编码为 26 个字符的字符串(与 36 个字符的 UUID 相比)。
注意
如果您在同一进程中的同一毫秒内生成多个 ULID,则随机部分将递增一位,以便为排序提供单调性。在这种情况下,随机部分与之前的 ULID 相比不是随机的。
生成 ULIDs
实例化 Ulid
类以生成随机 ULID 值
1 2 3
use Symfony\Component\Uid\Ulid;
$ulid = new Ulid(); // e.g. 01AN4Z07BY79KA1307SR9X4MV3
如果您的 ULID 值已以另一种格式生成,请使用以下任何方法从中创建 Ulid
对象
1 2 3 4 5 6
// all the following examples would generate the same Ulid object
$ulid = Ulid::fromString('01E439TP9XJZ9RPFH3T1PYBCR8');
$ulid = Ulid::fromBinary("\x01\x71\x06\x9d\x59\x3d\x97\xd3\x8b\x3e\x23\xd0\x6d\xe5\xb3\x08");
$ulid = Ulid::fromBase32('01E439TP9XJZ9RPFH3T1PYBCR8');
$ulid = Ulid::fromBase58('1BKocMc5BnrVcuq2ti4Eqm');
$ulid = Ulid::fromRfc4122('0171069d-593d-97d3-8b3e-23d06de5b308');
与 UUID 一样,ULID 也有自己的工厂 UlidFactory
,可用于生成它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
namespace App\Service;
use Symfony\Component\Uid\Factory\UlidFactory;
class FooService
{
public function __construct(
private UlidFactory $ulidFactory,
) {
}
public function generate(): void
{
$ulid = $this->ulidFactory->create();
// ...
}
}
还有一个特殊的 NilUlid
类来表示 ULID null
值
1 2 3 4
use Symfony\Component\Uid\NilUlid;
$ulid = new NilUlid();
// equivalent to $ulid = new Ulid('00000000000000000000000000');
转换 ULIDs
使用这些方法将 ULID 对象转换为不同的基数
1 2 3 4 5 6 7
$ulid = Ulid::fromString('01E439TP9XJZ9RPFH3T1PYBCR8');
$ulid->toBinary(); // string(16) "\x01\x71\x06\x9d\x59\x3d\x97\xd3\x8b\x3e\x23\xd0\x6d\xe5\xb3\x08"
$ulid->toBase32(); // string(26) "01E439TP9XJZ9RPFH3T1PYBCR8"
$ulid->toBase58(); // string(22) "1BKocMc5BnrVcuq2ti4Eqm"
$ulid->toRfc4122(); // string(36) "0171069d-593d-97d3-8b3e-23d06de5b308"
$ulid->toHex(); // string(34) "0x0171069d593d97d38b3e23d06de5b308"
使用 ULIDs
使用 Ulid
类创建的 ULID 对象可以使用以下方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\Uid\Ulid;
$ulid1 = new Ulid();
$ulid2 = new Ulid();
// checking if a given value is valid as ULID
$isValid = Ulid::isValid($ulidValue); // true or false
// getting the ULID datetime
$ulid1->getDateTime(); // returns a \DateTimeImmutable instance
// comparing ULIDs and checking for equality
$ulid1->equals($ulid2); // false
// this method returns $ulid1 <=> $ulid2
$ulid1->compare($ulid2); // e.g. int(-1)
在数据库中存储 ULIDs
如果您使用 Doctrine,请考虑使用 ulid
Doctrine 类型,它会自动转换为/从 ULID 对象转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Entity/Product.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UlidType;
use Symfony\Component\Uid\Ulid;
#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
#[ORM\Column(type: UlidType::NAME)]
private Ulid $someProperty;
// ...
}
还有一个 Doctrine 生成器,可帮助为实体主键自动生成 ULID 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Types\UlidType;
use Symfony\Component\Uid\Ulid;
class Product
{
#[ORM\Id]
#[ORM\Column(type: UlidType::NAME, unique: true)]
#[ORM\GeneratedValue(strategy: 'CUSTOM')]
#[ORM\CustomIdGenerator(class: 'doctrine.ulid_generator')]
private ?Ulid $id;
public function getId(): ?Ulid
{
return $this->id;
}
// ...
}
警告
出于性能原因,通常不建议将 ULID 用作主键。虽然 ULID 不会受到索引碎片问题的影响(因为值是顺序的),但它们的索引速度较慢且占用更多空间(因为二进制格式的 ULID 占用 128 位,而不是自动递增整数的 32/64 位)。
当使用内置 Doctrine 存储库方法(例如 findOneBy()
)时,Doctrine 知道如何转换这些 ULID 类型以构建 SQL 查询(例如 ->findOneBy(['user' => $user->getUlid()])
)。但是,当使用 DQL 查询或自己构建查询时,您需要将 ulid
设置为 ULID 参数的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
// src/Repository/ProductRepository.php
// ...
use Symfony\Bridge\Doctrine\Types\UlidType;
class ProductRepository extends ServiceEntityRepository
{
// ...
public function findUserProducts(User $user): array
{
$qb = $this->createQueryBuilder('p')
// ...
// add UlidType::NAME as the third argument to tell Doctrine that this is a ULID
->setParameter('user', $user->getUlid(), UlidType::NAME)
// alternatively, you can convert it to a value compatible with
// the type inferred by Doctrine
->setParameter('user', $user->getUlid()->toBinary())
;
// ...
}
}
在控制台中生成和检查 UUIDs/ULIDs
此组件提供了几个命令,用于在控制台中生成和检查 UUID/ULID。它们默认未启用,因此您必须在使用这些命令之前在应用程序中添加以下配置
1 2 3 4 5 6
# config/services.yaml
services:
Symfony\Component\Uid\Command\GenerateUlidCommand: ~
Symfony\Component\Uid\Command\GenerateUuidCommand: ~
Symfony\Component\Uid\Command\InspectUlidCommand: ~
Symfony\Component\Uid\Command\InspectUuidCommand: ~
现在您可以按如下方式生成 UUID/ULID(添加 --help
选项到命令以了解其所有选项)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# generate 1 random-based UUID
$ php bin/console uuid:generate --random-based
# generate 1 time-based UUID with a specific node
$ php bin/console uuid:generate --time-based=now --node=fb3502dc-137e-4849-8886-ac90d07f64a7
# generate 2 UUIDs and output them in base58 format
$ php bin/console uuid:generate --count=2 --format=base58
# generate 1 ULID with the current time as the timestamp
$ php bin/console ulid:generate
# generate 1 ULID with a specific timestamp
$ php bin/console ulid:generate --time="2021-02-02 14:00:00"
# generate 2 ULIDs and output them in RFC4122 format
$ php bin/console ulid:generate --count=2 --format=rfc4122
除了生成新的 UID 之外,您还可以使用以下命令检查它们,以显示给定 UID 的所有信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
$ php bin/console uuid:inspect d0a3a023-f515-4fe0-915c-575e63693998
---------------------- --------------------------------------
Label Value
---------------------- --------------------------------------
Version 4
Canonical (RFC 4122) d0a3a023-f515-4fe0-915c-575e63693998
Base 58 SmHvuofV4GCF7QW543rDD9
Base 32 6GMEG27X8N9ZG92Q2QBSHPJECR
---------------------- --------------------------------------
$ php bin/console ulid:inspect 01F2TTCSYK1PDRH73Z41BN1C4X
--------------------- --------------------------------------
Label Value
--------------------- --------------------------------------
Canonical (Base 32) 01F2TTCSYK1PDRH73Z41BN1C4X
Base 58 1BYGm16jS4kX3VYCysKKq6
RFC 4122 0178b5a6-67d3-0d9b-889c-7f205750b09d
--------------------- --------------------------------------
Timestamp 2021-04-09 08:01:24.947
--------------------- --------------------------------------