Компонент UID

Дата обновления перевода 2024-07-10

Компонент UID

Компонент UID предоставляет утилиты для работы с уникальными идентификаторами (UID), вроде UUID и ULID.

Установка

1
$ composer require symfony/uid

Note

Если вы устанавливаете этот компонент вне приложения Symfony, вам нужно подключить файл vendor/autoload.php в вашем коде для включения механизма автозагрузки классов, предоставляемых Composer. Детальнее читайте в этой статье.

UUID

UUID (универсально-уникальные идентификаторы) - это одни из наиболее популярных UID в индустрии ПО. UUID - это 128-битные числа, обычно представленные в виде 5 групп шестнадцатеричных символов: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx (знак M - UUID-версия, а знак N - UUID-вариант).

Создание UUID

Используйте именованные конструкторы класса Uuid или любых других конкретных классов, чтобы создать каждый тип UUID:

UUID v1 (основанный на времени)

Генерирует UUID, используя временную метку и MAC-адрес вашего устройства (прочтите спецификацию UUIDv1). Оба параметра получаются автоматически, поэтому вам не нужно передавать какой-либо аргумент конструктора:

1
2
3
4
use Symfony\Component\Uid\Uuid;

// $uuid - это экземпляр Symfony\Component\Uid\UuidV1
$uuid = Uuid::v1();

Tip

Рекомендуется использовать UUIDv7 вместо UUIDv1, поскольку он обеспечивает лучшую энтропию.

UUID v2 (DCE безопасность)

Похож на UUIDv1, но с очень высокой вероятностью столкновения идентификаторов. (читайте спецификацию UUIDv2). Это часть механизма аутентификации в DCE ("Распределенная вычислительная среда"). и UUID включает в себя POSIX UID (идентификатор пользователя/группы) пользователя, который его сгенерировал. Этот вариант UUID не реализуется компонентом Uid.

UUID v3 (основанный на имени, MD5)

Генерирует UUID из имен, которые принадлежат и являются уникальными в пределах некоторого заданного пространства имен (читайте спецификацию UUIDv3). Этот вариант полезен для генерирования детерминированных UUID из произвольных строк. Он работает путем заполнения содержимого UUID хэшем md5, полученным в результате конкатенации пространства имен и имени:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\Uid\Uuid;

// вы можете использовать любое из предварительно определенных пространств имен...
$namespace = Uuid::fromString(Uuid::NAMESPACE_OID);
// ...или использовать произвольное пространство имен:
// $namespace = Uuid::v4();

// $name может быть любой произвольной строкой
// $uuid - это экземпляр Symfony\Component\Uid\UuidV3
$uuid = Uuid::v3($namespace, $name);

Вот пространства имен, определенные стандартом по умолчанию:

UUID v4 (произвольный)

Генерирует произвольный UUID (читайте спецификацию UUIDv4). Благодаря своей случайности он обеспечивает уникальность в распределенных системах без необходимости в центральном координирующем органе. Он безопасен для конфиденциальности, поскольку не содержит никакой информации о том, где и когда он был сгенерирован:

use SymfonyComponentUidUuid;

// $uuid - это экземпляр SymfonyComponentUidUuidV4 $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 - это экземпляр Symfony\Component\Uid\UuidV6
$uuid = Uuid::v6();

Tip

Рекомендуется использовать UUIDv7 вместо UUIDv6, поскольку он обеспечивает лучшую энтропию.

UUID v7 (временная метка UNIX)

Генерирует упорядоченные по времени UUID на основе источника временной метки высокого разрешения Unix Epoch (количество миллисекунд, прошедших с полуночи 1 января 1970 года по UTC, без учета високосных секунд) (читайте спецификацию UUIDv7). Рекомендуется использовать эту версию вместо UUIDv1 и UUIDv6, поскольку она обеспечивает лучшую энтропию (и более строгий хронологический порядок генерирования UUID):

1
2
3
4
use Symfony\Component\Uid\Uuid;

// $uuid - это экземпляр Symfony\Component\Uid\UuidV7
$uuid = Uuid::v7();

UUID v8 (пользовательский)

Предоставляет RFC-совместимый формат для экспериментальных или специфических для поставщика случаев использования.
(читайте спецификацию UUIDv8). Единственным требованием является установка частей варианта и версии в UUID. Остальное значение UUID зависит от конкретной реализации, и не следует предполагать никакого формата:

1
2
3
4
use Symfony\Component\Uid\Uuid;

// $uuid - это экземпляр Symfony\Component\Uid\UuidV8
$uuid = Uuid::v8();

Если ваше значение UUID уже сгенировано в другом формате, используйте любой из следующих методов, чтобы создать из него объект Uuid:

1
2
3
4
5
6
// все следующие примеры сгенерируют одинаковый объект Uuid
$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
    {
        // Это создаёт UUID версии, заданной в файле конфигурации (v6 по умолчанию)
        $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
// преобразовать V1 в V6 или V7
$uuid = Uuid::v1();

$uuid->toV6(); // возвращает экземпляр Symfony\Component\Uid\UuidV6
$uuid->toV7(); // возвращает экземпляр Symfony\Component\Uid\UuidV7

// преобразовать V6 в V7
$uuid = Uuid::v6();

$uuid->toV7(); // возвращает экземпляр Symfony\Component\Uid\UuidV7

7.1

Методы toV6(), toV7() и toV7() были представлены в Symfony 7.1.

Работа с UUID

UUID-объекты, созданные с классом Uuid могут использовать следующие методы (которые эквивалентны методы uuid_*() PHP-расширения):

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;

// проверка, является ли UUID null (отметьте, что класс называется
// NilUuid вместо NullUuid, чтобы следовать cтандартной нотации UUID)
$uuid = Uuid::v4();
$uuid instanceof NilUuid; // false

// проверка типа UUID
use Symfony\Component\Uid\UuidV4;
$uuid = Uuid::v4();
$uuid instanceof UuidV4; // true

// получение даты и времени UUID (доступно только в некоторых типах UUID)
$uuid = Uuid::v1();
$uuid->getDateTime();  // returns a \DateTimeImmutable instance

// проверка, является ли заданное значение валидным как UUID
$isValid = Uuid::isValid($uuid); // true or false

// сравнение UUID и проверка равенства
$uuid1 = Uuid::v1();
$uuid4 = Uuid::v4();
$uuid1->equals($uuid4); // false

// этот метод возвращает:
//   * int(0), если $uuid1 и $uuid4 равны
//   * int > 0, если $uuid1 больше, чем $uuid4
//   * int < 0, если $uuid1 меньше, чем $uuid4
$uuid1->compare($uuid4); // например, int(4)

Хранение UUID в базах данных

Если вы используете Doctrine, рассмотрите использование типа Doctrine uuid, который преобразует из/в объекты 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;
    }

    // ...
}

При использовании встроенных методов хранилища 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
// src/Repository/ProductRepository.php

// ...
use Symfony\Bridge\Doctrine\Types\UuidType;

class ProductRepository extends ServiceEntityRepository
{
    // ...

    public function findUserProducts(User $user): array
    {
        $qb = $this->createQueryBuilder('p')
            // ...
            // добавьте 'uuid' как третий аргумент, чтобы сообщить Doctrine, что это UUID
            ->setParameter('user', $user->getUuid(), 'uuid')

            // как вариант, вы можете преобразовать его в значение, совместимое с
            // типом, определенным Doctrine
            ->setParameter('user', $user->getUuid()->toBinary())
        ;

        // ...
    }
}

ULID

ULID (Универсально-уникальные лексикографически сортируемые идентификаторы) - это 128-битные числа, обычно представленные в виде строк из 26 знаком: TTTTTTTTTTRRRRRRRRRRRRRRRR (где T - это временная отметка, а R - рандомные биты).

ULID - это альтернатива UUID, когда использование последних непрактично. Они предоставляет 128-битную совместимость с UUID, лексикографически сортируемы и зашифрованы как строки из 26 знаков (против 36-знаковых UUID).

Note

Если вы генерируете более одного ULID во время одной и той же миллисекунды в одном процессе, тогда рандомная часть увеличивается на один бит, чтобы предоставить монотонность для сортировки. В этом случае, рандомная часть не настолько случайна, по сравнению с предыдущим ULID.

Создание ULID

Инстанциируйте класс Ulid, чтобы сгенерировать рандомное значение ULID:

1
2
3
use Symfony\Component\Uid\Ulid;

$ulid = new Ulid();  // например, 01AN4Z07BY79KA1307SR9X4MV3

Если ваше значение ULID уже сгенерировано в другом формате, используйте любой из следующих методов, чтобы создать из него объект Ulid:

1
2
3
4
5
6
// все следующие примеры создадут одинаковый объект Ulid
$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();
// эквивалентно $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();

// проверка, является ли заданное значение валидным как ULID
$isValid = Ulid::isValid($ulidValue); // true or false

// получение времени и даты ULID
$ulid1->getDateTime(); // returns a \DateTimeImmutable instance

// сравнение ULID и проверка равенства
$ulid1->equals($ulid2); // false
// this method returns $ulid1 <=> $ulid2
$ulid1->compare($ulid2); // e.g. int(-1)

Хранение ULID в базах данных

Если вы используете Doctrine, рассмотрите использование типа Doctrine ulid, который автотматически преобразует в/из 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
22
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;
    }

    // ...

}

При использовании встроенных методов хранилища 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')
            // ...
            // добавьте 'ulid' как третий аргумент, чтобы сообщить Doctrine, что это ULID
            ->setParameter('user', $user->getUlid(), 'ulid')

            // как вариант, вы можете преобразовать его в значение, совместимое с
            // типом, определенным 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
# сгенерировать 1 рандомно-основанный UUID
$ php bin/console uuid:generate --random-based

# сгенерировать 1 UUID, основанный на времени, с конкретным узлом
$ php bin/console uuid:generate --time-based=now --node=fb3502dc-137e-4849-8886-ac90d07f64a7

# сгенерировать 2 UUID и вывести их в формате base58
$ php bin/console uuid:generate --count=2 --format=base58

# сгенерировать 1 ULID с текущем временем в качестве временной отметки
$ php bin/console ulid:generate

# сгенерировать 1 ULID с конкретной временной отметкой
$ php bin/console ulid:generate --time="2021-02-02 14:00:00"

# сгенерировать 2 ULID и вывести их в формате RFC4122
$ 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
 ------------------------- --------------------------------------
  Ярлык                     Значение
 ------------------------- --------------------------------------
  Версия                    4
  Каноничность (RFC 4122)   d0a3a023-f515-4fe0-915c-575e63693998
  База 58                   SmHvuofV4GCF7QW543rDD9
  База 32                   6GMEG27X8N9ZG92Q2QBSHPJECR
 ------------------------- --------------------------------------

$ php bin/console ulid:inspect 01F2TTCSYK1PDRH73Z41BN1C4X
 ------------------------ --------------------------------------
  Ярлык                    Значение
 ------------------------ --------------------------------------
  Каноникал (База 32)      01F2TTCSYK1PDRH73Z41BN1C4X
  База 58                  1BYGm16jS4kX3VYCysKKq6
  RFC 4122                 0178b5a6-67d3-0d9b-889c-7f205750b09d
 ------------------------ --------------------------------------
  Временная отметка        2021-04-09 08:01:24.947
 ------------------------ --------------------------------------