uma/uuid

轻量级的UUID库

v3.0.0 2023-09-24 18:50 UTC

This package is auto-updated.

Last update: 2024-08-24 20:45:05 UTC


README

CI Code Coverage

PHP +8.2的UUID规范的SOLID和轻量级实现

project overview

安装

$ 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构建?

因为Version1GeneratorCombGenerator代码的几个部分依赖于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扩展,它将对运行时性能产生巨大影响,从而歪曲结果。