Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 5d28b73

Browse files
[Uid] fix performance and prevent collisions with the real clock_seq
1 parent d583b4f commit 5d28b73

File tree

6 files changed

+52
-27
lines changed

6 files changed

+52
-27
lines changed

src/Symfony/Component/Uid/NilUuid.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,6 @@ class NilUuid extends Uuid
2020

2121
public function __construct()
2222
{
23-
$this->uid = '00000000-0000-0000-0000-000000000000';
23+
$this->uid = parent::NIL;
2424
}
2525
}

src/Symfony/Component/Uid/Tests/Factory/UuidFactoryTest.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ public function testCreateTimed()
6565
$this->assertSame('c10ab929ae84', $uuid1->getNode());
6666

6767
// Test default node override
68-
$uuid2 = $uuidFactory->timeBased('7c1ede70-3586-48ed-a984-23c8018d9174')->create();
69-
$this->assertInstanceOf(UuidV6::class, $uuid2);
70-
$this->assertSame('23c8018d9174', $uuid2->getNode());
68+
$uuid2Factory = $uuidFactory->timeBased('7c1ede70-3586-48ed-a984-23c8018d9174');
69+
$this->assertSame('1eb5a7ae-17e1-62d0-a984-23c8018d9174', (string) $uuid2Factory->create(new \DateTime('@1611076938.057800')));
70+
$this->assertSame('23c8018d9174', substr($uuid2Factory->create(), 24));
71+
$this->assertNotSame('a984', substr($uuid2Factory->create(), 19, 4));
7172

7273
// Test version override
7374
$uuid3 = (new UuidFactory(6, 1))->timeBased()->create();

src/Symfony/Component/Uid/Ulid.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
*/
2121
class Ulid extends AbstractUid
2222
{
23+
private const NIL = '00000000000000000000000000';
24+
2325
private static $time = '';
2426
private static $rand = [];
2527

@@ -31,6 +33,12 @@ public function __construct(string $ulid = null)
3133
return;
3234
}
3335

36+
if (self::NIL === $ulid) {
37+
$this->uid = $ulid;
38+
39+
return;
40+
}
41+
3442
if (!self::isValid($ulid)) {
3543
throw new \InvalidArgumentException(sprintf('Invalid ULID: "%s".', $ulid));
3644
}
@@ -77,7 +85,10 @@ public static function fromString(string $ulid): parent
7785
base_convert(substr($ulid, 27, 5), 16, 32)
7886
);
7987

80-
return new static(strtr($ulid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ'));
88+
$u = new static(self::NIL);
89+
$u->uid = strtr($ulid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ');
90+
91+
return $u;
8192
}
8293

8394
public function toBinary(): string

src/Symfony/Component/Uid/Uuid.php

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,17 @@ class Uuid extends AbstractUid
2424
public const NAMESPACE_X500 = '6ba7b814-9dad-11d1-80b4-00c04fd430c8';
2525

2626
protected const TYPE = 0;
27+
protected const NIL = '00000000-0000-0000-0000-000000000000';
2728

2829
public function __construct(string $uuid)
2930
{
30-
$type = uuid_is_valid($uuid) ? uuid_type($uuid) : false;
31+
$type = preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid) ? (int) $uuid[14] : false;
3132

32-
if (false === $type || \UUID_TYPE_INVALID === $type || (static::TYPE ?: $type) !== $type) {
33+
if (false === $type || (static::TYPE ?: $type) !== $type) {
3334
throw new \InvalidArgumentException(sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
3435
}
3536

36-
$this->uid = strtr($uuid, 'ABCDEF', 'abcdef');
37+
$this->uid = strtolower($uuid);
3738
}
3839

3940
/**
@@ -60,17 +61,16 @@ public static function fromString(string $uuid): parent
6061
return new static($uuid);
6162
}
6263

63-
if (!uuid_is_valid($uuid)) {
64-
throw new \InvalidArgumentException(sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
64+
if (self::NIL === $uuid) {
65+
return new NilUuid();
6566
}
6667

67-
switch (uuid_type($uuid)) {
68+
switch ($uuid[14]) {
6869
case UuidV1::TYPE: return new UuidV1($uuid);
6970
case UuidV3::TYPE: return new UuidV3($uuid);
7071
case UuidV4::TYPE: return new UuidV4($uuid);
7172
case UuidV5::TYPE: return new UuidV5($uuid);
7273
case UuidV6::TYPE: return new UuidV6($uuid);
73-
case NilUuid::TYPE: return new NilUuid();
7474
}
7575

7676
return new self($uuid);
@@ -109,11 +109,11 @@ final public static function v6(): UuidV6
109109

110110
public static function isValid(string $uuid): bool
111111
{
112-
if (__CLASS__ === static::class) {
113-
return uuid_is_valid($uuid);
112+
if (!preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid)) {
113+
return false;
114114
}
115115

116-
return uuid_is_valid($uuid) && static::TYPE === uuid_type($uuid);
116+
return __CLASS__ === static::class || static::TYPE === (int) $uuid[14];
117117
}
118118

119119
public function toBinary(): string

src/Symfony/Component/Uid/UuidV1.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class UuidV1 extends Uuid
2020
{
2121
protected const TYPE = 1;
2222

23+
private static $clockSeq;
24+
2325
public function __construct(string $uuid = null)
2426
{
2527
if (null === $uuid) {
@@ -41,11 +43,25 @@ public function getNode(): string
4143

4244
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string
4345
{
44-
$uuid = uuid_create(static::TYPE);
46+
$uuid = !$time || !$node ? uuid_create(static::TYPE) : parent::NIL;
47+
48+
if ($time) {
49+
if ($node) {
50+
// use clock_seq from the node
51+
$seq = substr($node->uid, 19, 4);
52+
} else {
53+
// generate a static random clock_seq to prevent any collisions with the real one
54+
$seq = substr($uuid, 19, 4);
55+
56+
while (null === self::$clockSeq || $seq === self::$clockSeq) {
57+
self::$clockSeq = sprintf('%04x', random_int(0, 0x3fff) | 0x8000);
58+
}
59+
60+
$seq = self::$clockSeq;
61+
}
4562

46-
if (null !== $time) {
4763
$time = BinaryUtil::dateTimeToHex($time);
48-
$uuid = substr($time, 8).'-'.substr($time, 4, 4).'-1'.substr($time, 1, 3).substr($uuid, 18);
64+
$uuid = substr($time, 8).'-'.substr($time, 4, 4).'-1'.substr($time, 1, 3).'-'.$seq.substr($uuid, 23);
4965
}
5066

5167
if ($node) {

src/Symfony/Component/Uid/UuidV6.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class UuidV6 extends Uuid
2222
{
2323
protected const TYPE = 6;
2424

25-
private static $seed;
25+
private static $node;
2626

2727
public function __construct(string $uuid = null)
2828
{
@@ -55,15 +55,12 @@ public static function generate(\DateTimeInterface $time = null, Uuid $node = nu
5555
// uuid_create() returns a stable "node" that can leak the MAC of the host, but
5656
// UUIDv6 prefers a truly random number here, let's XOR both to preserve the entropy
5757

58-
if (null === self::$seed) {
59-
self::$seed = [random_int(0, 0xffffff), random_int(0, 0xffffff)];
58+
if (null === self::$node) {
59+
$seed = [random_int(0, 0xffffff), random_int(0, 0xffffff)];
60+
$node = unpack('N2', hex2bin('00'.substr($uuidV1, 24, 6)).hex2bin('00'.substr($uuidV1, 30)));
61+
self::$node = sprintf('%06x%06x', ($seed[0] ^ $node[1]) | 0x010000, $seed[1] ^ $node[2]);
6062
}
6163

62-
$node = unpack('N2', hex2bin('00'.substr($uuidV1, 24, 6)).hex2bin('00'.substr($uuidV1, 30)));
63-
64-
return $uuid.sprintf('%06x%06x',
65-
(self::$seed[0] ^ $node[1]) | 0x010000,
66-
self::$seed[1] ^ $node[2]
67-
);
64+
return $uuid.self::$node;
6865
}
6966
}

0 commit comments

Comments
 (0)