uma / uuid
轻量级的UUID库
Requires
- php-64bit: ^8.2.0 || ^8.3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.28
- phpbench/phpbench: ^1.2
- phpunit/phpunit: ^10.3
- scrutinizer/ocular: ^1.9
This package is auto-updated.
Last update: 2024-08-24 20:45:05 UTC
README
PHP +8.2的UUID规范的SOLID和轻量级实现
安装
$ composer require uma/uuid
生成器
RFC 4122版本1
$v1 = new \UMA\Uuid\Version1Generator('01:23:45:67:89:ab'); (string) $v1->generate(); // c1f45316-dd26-11e7-b25b-0123456789ab (string) $v1->generate(); // c1f45af4-dd26-11e7-b25b-0123456789ab (string) $v1->generate(); // c1f45bf8-dd26-11e7-b25b-0123456789ab
如何选择'clockSeq'参数?
每次在Version1Generator实例上调用generate()
方法时,都会生成一个全新的、随机的14位值。
此行为符合RFC规范(见第4.1.5节)。
RFC 4122版本4
$v4 = new \UMA\Uuid\Version4Generator(); (string) $v4->generate(); // 89f18abf-4c99-488c-a7e5-4f3a58e39d44 (string) $v4->generate(); // ff02c008-3318-4464-8f7c-0fc9263830b4 (string) $v4->generate(); // d4d33558-4a48-469e-8456-a46c4c895aac
RFC 4122版本5
$ns = \UMA\Uuid\Uuid::fromString(\UMA\Uuid\Version5Generator::NS_DNS); $v5 = new \UMA\Uuid\Version5Generator($ns); (string) $v5->generate('foo'); // b84ed8ed-a7b1-502f-83f6-90132e68adef (string) $v5->generate('bar'); // e8d5cf6d-de0f-5e77-9aa3-91093cdfbf62
COMB生成器
此生成器不属于RFC 4122规范。它是一种版本4 UUID的变体,据我所知,最早由Jimmy Nilsson于2002年在一篇名为The Cost of GUIDs as Primary Keys的文章中描述。
COMB UUIDs只是版本4 UUID,其48位高比特已被时间戳覆盖。这在一定程度上保证了生成的值是单调的(不断增长),当这些值用作数据库主键(或简单索引)时,这是一个非常期望的特性。
$comb = new \UMA\Uuid\CombGenerator(); (string) $comb->generate(); // 55fef06f-9382-4732-96b4-c13f92f7f254 (string) $comb->generate(); // 55fef06f-9392-4f41-a4c7-e036c1befcd7 (string) $comb->generate(); // 55fef06f-9396-4904-a606-c9b62c8e7aea
构造函数中的可选$granularity参数是什么意思?
粒度反映了将印在UUID上的时间戳的精度,实际上是从microtime(true)
调用中使用的十进制位数。
var_dump(microtime(true)); // 1513212567.378735
此数字可以从0(表示秒精度)到6(表示微秒精度,PHP能处理的最大值,也是默认值)。
由于使用相同时间戳生成的多个COMB UUID不保证是单调的,因此粒度越高,保证就越强,CombGenerator
的输出就越好。为了提供一些参考,我的笔记本电脑可以在同一秒内生成超过100k个COMB UUID,但在同一微秒内只能生成1或2个。
当高48位达到ffffffff-ffff
时会发生什么?
生成器将溢出,并从10000000-0000
开始。当然,这意味着在这个时候,生成器的单调特性将丢失!
何时发生溢出?
这取决于您选择的粒度,但getOverflowDate()
方法返回它将发生的确切时刻。
当我写这篇文章时,最近的一次溢出将发生在2059-03-13 02:56:07 UTC(粒度为5)。
$g6 = new \UMA\Uuid\CombGenerator(6); $g6->getOverflowDate(); // object(DateTimeImmutable)#4 (3) { // ["date"]=> // string(26) "2112-09-17 23:53:47.000000" // ["timezone_type"]=> // int(1) // ["timezone"]=> // string(6) "+00:00" // } $g0 = new \UMA\Uuid\CombGenerator(0); $g0->getOverflowDate(); // object(DateTimeImmutable)#6 (3) { // ["date"]=> // string(29) "8921556-12-07 10:44:15.000000" // ["timezone_type"]=> // int(1) // ["timezone"]=> // string(6) "+00:00" // }
顺序生成器(不安全)
SequentialGenerator
根本不会生成唯一的UUID。它可以用于测试环境中,作为真实生成器的替代品,在这种情况下,可能需要可读性和确定性值。
生成器的第一个参数将在其UUID的较高8个字节上打印一个"模式"。第二个是计数开始的偏移量。两者都是可选的。
$vanilla = new \UMA\Uuid\SequentialGenerator(); (string) $vanilla->generate(); // 00000000-0000-0000-0000-000000000000 (string) $vanilla->generate(); // 00000000-0000-0000-0000-000000000001 (string) $vanilla->generate(); // 00000000-0000-0000-0000-000000000002 $custom = new \UMA\Uuid\SequentialGenerator(dechex('abcd'), 255); (string) $custom->generate(); // 00000000-0000-abcd-0000-0000000000ff (string) $custom->generate(); // 00000000-0000-abcd-0000-000000000100 (string) $custom->generate(); // 00000000-0000-abcd-0000-000000000101
Uuid类
Uuid
类被建模为一个值对象,它包装有效的UUID字符串。它还有命名构造函数,用于在预先知道预期值时生成Uuid对象。
从字符串创建Uuid对象
Uuid::fromString(string): Uuid
这是在您有一个UUID值在字符串变量中时创建新实例的首选方法。该方法不区分大小写。
// Creates a new Uuid object from a hardcoded string $uuid = \UMA\Uuid::fromString('96aaab69-7b76-4461-b008-cbb9cfcb6fdf'); // Passing an invalid value will result in an \InvalidArgumentException thrown $badUuid = \UMA\Uuid::fromString('abcd'); // If you don't know beforehand if the value is a valid UUID but want // to avoid the exception you can rely on the `Uuid::isUuid(string): bool` helper. if (false === \UMA\Uuid::isUuid($unsafeUuid)) { return; } $safeUuid = \UMA\Uuid::fromString($unsafeUuid);
Uuid::fromBytes(string): Uuid
从16个原始字节创建新的Uuid实例。这个工厂被生成器广泛使用,但它也可以覆盖一些库最终用户的用例,例如从数据库中检索打包形式的UUID字符串。
$uuid = \UMA\Uuid::fromBytes(random_bytes(16));
Uuid::nil(): Uuid
用于生成NIL UUID实例的便利辅助函数。
获取Uuid对象的内部值
Uuid
对象有两个getter方法:asString()
和asBytes()
。前者返回UUID的规范文本格式,后者以16字节的原始序列形式返回。
它们也可以被转换为字符串。这等价于调用asString()
方法。
$uuid = (new \UMA\Uuid\CombGenerator)->generate(); $uuid->asBytes(); // ??????M???D????d $uuid->asString(); // 5608bfbf-4602-4abb-9aa1-7584ab428631 (string) $uuid === $uuid->asString(); // true
常见问题解答(FAQ)
哪种类型的UUID“最好”?我应该使用哪一种?
UUID通常用作关系数据库表中代理键。这意味着它们被索引,索引性能部分取决于这些值的连续性。因此,“最好”的UUID类型是COMB UUID。
我在Postgres 9.6上运行了一个基准测试,测量了插入不同类型的预生成一亿个UUID所需的时间。版本4 UUID插入到表中的速度比传统的自增整数慢约32倍。相比之下,COMB UUID“只是”慢了大约40%。这些结果与15年前Nilsson先生在其文章中报告的结果一致。
总之,除非你有特定的理由使用其他类型的UUID,否则你应该使用COMB UUID。
Version3Generator在哪里?
由于它与版本5完全相同,但依赖于更弱的哈希算法,我看不出添加它的必要性。
Version2Generator在哪里?
据我所知,版本2 UUID从未被定义。《规范》甚至没有提及它们。自己去想吧。
为什么1ma/uuid
特别需要64位PHP构建?
因为Version1Generator
和CombGenerator
代码的几个部分依赖于64位宽的整数(特别是,将microtime(true)
的输出转换为整数的可能最多填充56位,在撰写本文时)。
如果你的PHP二进制文件是为32位架构构建的,但你不想使用这些基于时间的生成器,你仍然可以使用这个库,但需要通过传递--ignore-platform-reqs
标志到composer来安装它。
我能自己编写生成器吗?
是的,只需实现UuidGenerator
接口。这里有一个总是返回相同Uuid的示例实现。这可以在单元测试环境中很有用。
use UMA\Uuid\Uuid; use UMA\Uuid\UuidGenerator; class DeterministicGenerator implements UuidGenerator { /** * @var Uuid */ private $uuid; public function __construct(Uuid $uuid) { $this->uuid = $uuid; } public function generate(string $name = null): Uuid { return clone $this->uuid; } }
我如何运行测试?
$ composer test
我如何运行基准测试?
$ composer bench
在PHP中,如果启用了xdebug
扩展,它将对运行时性能产生巨大影响,从而歪曲结果。