跳到內容

UID 元件

編輯此頁

UID 元件提供了實用工具來處理唯一識別碼 (UID),例如 UUID 和 ULID。

安裝

1
$ composer require symfony/uid

注意

如果您在 Symfony 應用程式之外安裝此元件,您必須在程式碼中引用 vendor/autoload.php 檔案,以啟用 Composer 提供的類別自動載入機制。請閱讀這篇文章以取得更多詳細資訊。

UUID

UUID (通用唯一識別碼) 是軟體產業中最受歡迎的 UID 之一。UUID 是 128 位元的數字,通常表示為五組十六進位字元:xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxxM 位數是 UUID 版本,而 N 位數是 UUID 變體)。

產生 UUID

使用 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();

        // ...
    }
}

轉換 UUID

使用這些方法將 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

7.1

toV6()toV7()toV7() 方法在 Symfony 7.1 中引入。

使用 UUID

使用 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 中引入。

在資料庫中儲存 UUID

如果您使用 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 v6UUID 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)
        ;

        // ...
    }
}

ULID

ULID (通用唯一字典編排可排序識別碼) 是 128 位元的數字,通常表示為 26 個字元的字串:TTTTTTTTTTRRRRRRRRRRRRRRRR (其中 T 代表時間戳記,而 R 代表隨機位元)。

當使用 UUID 不切實際時,ULID 是 UUID 的替代方案。它們提供與 UUID 的 128 位元相容性,它們在字典編排上可排序,並且它們編碼為 26 個字元的字串 (相較於 36 個字元的 UUID)。

注意

如果您在同一個程序中的同一個毫秒內產生多個 ULID,則隨機部分會遞增一位元,以便為排序提供單調性。在這種情況下,隨機部分與先前的 ULID 相比並非隨機。

產生 ULID

實例化 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');

轉換 ULID

使用這些方法將 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"

使用 ULID

使用 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)

在資料庫中儲存 ULID

如果您使用 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())
        ;

        // ...
    }
}

在主控台中產生和檢查 UUID/ULID

此元件提供多個命令,可在主控台中產生和檢查 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
 --------------------- --------------------------------------
此作品 (包括程式碼範例) 依據 Creative Commons BY-SA 3.0 授權條款授權。
目錄
    版本