8000 [Uid] Add `UuidV1::toV6()`, `UuidV1::toV7()` and `UuidV6::toV7()` · symfony/symfony@9ee1437 · GitHub
[go: up one dir, main page]

Skip to content
10000

Commit 9ee1437

Browse files
nicolas-grekasfancyweb
authored andcommitted
[Uid] Add UuidV1::toV6(), UuidV1::toV7() and UuidV6::toV7()
1 parent 93f1812 commit 9ee1437

File tree

5 files changed

+62
-37
lines changed

5 files changed

+62
-37
lines changed

src/Symfony/Component/Uid/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ CHANGELOG
44
7.1
55
---
66

7-
* Add `UuidV6::fromV1()` and `UuidV7::fromV1()`
7+
* Add `UuidV1::toV6()`, `UuidV1::toV7()` and `UuidV6::toV7()`
88

99
6.2
1010
---

src/Symfony/Component/Uid/Tests/UuidTest.php

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -428,32 +428,47 @@ public function testFromStringBase58Padding()
428428
$this->assertInstanceOf(Uuid::class, Uuid::fromString('111111111u9QRyVM94rdmZ'));
429429
}
430430

431-
public function testV6FromV1()
431+
public function testV1ToV6()
432432
{
433433
$uuidV1 = new UuidV1('8189d3de-9670-11ee-b9d1-0242ac120002');
434-
$uuidV6 = UuidV6::fromV1($uuidV1);
434+
$uuidV6 = $uuidV1->toV6();
435435

436436
$this->assertEquals($uuidV1->getDateTime(), $uuidV6->getDateTime());
437437
$this->assertSame($uuidV1->getNode(), $uuidV6->getNode());
438-
$this->assertEquals($uuidV6, UuidV6::fromV1($uuidV1));
438+
$this->assertEquals($uuidV6, $uuidV1->toV6());
439439
}
440440

441-
public function testV7FromV1BeforeUnixEpochThrows()
441+
public function testV1ToV7BeforeUnixEpochThrows()
442442
{
443443
$this->expectException(\InvalidArgumentException::class);
444-
$this->expectExceptionMessage('UUIDv1 with a timestamp before Unix epoch cannot be converted to UUIDv7');
444+
$this->expectExceptionMessage('Cannot convert UUID to v7: its timestamp is before 10000 the Unix epoch.');
445445

446-
UuidV7::fromV1(new UuidV1('9aba8000-ff00-11b0-b3db-3b3fc83afdfc')); // Timestamp is 1969-01-01 00:00:00.0000000
446+
(new UuidV1('9aba8000-ff00-11b0-b3db-3b3fc83afdfc'))->toV7(); // Timestamp is 1969-01-01 00:00:00.0000000
447447
}
448448

449-
public function testV7FromV1()
449+
public function testV1ToV7()
450450
{
451451
$uuidV1 = new UuidV1('eb248d80-ea4f-11ec-9d2a-839425e6fb88');
452452
$sameUuidV1100NanosecondsLater = new UuidV1('eb248d81-ea4f-11ec-9d2a-839425e6fb88');
453-
$uuidV7 = UuidV7::fromV1($uuidV1);
453+
$uuidV7 = $uuidV1->toV7();
454+
$sameUuidV7100NanosecondsLater = $sameUuidV1100NanosecondsLater->toV7();
454455

455-
$this->assertEquals($uuidV1->getDateTime(), $uuidV7->getDateTime());
456-
$this->assertEquals($uuidV7, UuidV7::fromV1($uuidV1));
457-
$this->assertNotEquals($uuidV7, UuidV7::fromV1($sameUuidV1100NanosecondsLater));
456+
$this->assertSame($uuidV1->getDateTime()->format('Uv'), $uuidV7->getDateTime()->format('Uv'));
457+
$this->assertEquals($uuidV7, $uuidV1->toV7());
458+
$this->assertNotEquals($uuidV7, $sameUuidV7100NanosecondsLater);
459+
$this->assertSame(hexdec('0'.substr($uuidV7, -2)) + 1, hexdec('0'.substr($sameUuidV7100NanosecondsLater, -2)));
460+
}
461+
462+
public function testV1ToV7WhenExtraTimeEntropyOverflows()
463+
{
464+
$uuidV1 = new UuidV1('10e7718f-2d4f-11be-bfed-cdd35907e584');
465+
$sameUuidV1100NanosecondsLater = new UuidV1('10e77190-2d4f-11be-bfed-cdd35907e584');
466+
$uuidV7 = $uuidV1->toV7();
467+
$sameUuidV7100NanosecondsLater = $sameUuidV1100NanosecondsLater->toV7();
468+
469+
$this->assertSame($uuidV1->getDateTime()->format('Uv'), $uuidV7->getDateTime()->format('Uv'));
470+
$this->assertEquals($uuidV7, $uuidV1->toV7());
471+
$this->assertNotEquals($uuidV7, $sameUuidV7100NanosecondsLater);
472+
$this->assertSame(hexdec('0'.substr($uuidV7, -2)) + 1, hexdec('0'.substr($sameUuidV7100NanosecondsLater, -2)));
458473
}
459474
}

src/Symfony/Component/Uid/UuidV1.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ public function getNode(): string
4141
return uuid_mac($this->uid);
4242
}
4343

44+
public function toV6(): UuidV6
45+
{
46+
$uuid = $this->uid;
47+
48+
return new UuidV6(substr($uuid, 15, 3).substr($uuid, 9, 4).$uuid[0].'-'.substr($uuid, 1, 4).'-6'.substr($uuid, 5, 3).substr($uuid, 18, 6).substr($uuid, 24));
49+
}
50+
51+
public function toV7(): UuidV7
52+
{
53+
return $this->toV6()->toV7();
54+
}
55+
4456
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string
4557
{
4658
$uuid = !$time || !$node ? uuid_create(static::TYPE) : parent::NIL;

src/Symfony/Component/Uid/UuidV6.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,31 @@ public function getNode(): string
4343
return substr($this->uid, 24);
4444
}
4545

46-
public static function fromV1(UuidV1 $uuidV1): self
46+
public function toV7(): UuidV7
4747
{
48-
$uuidV1 = $uuidV1->toRfc4122();
48+
$uuid = $this->uid;
49+
$time = BinaryUtil::hexToNumericString('0'.substr($uuid, 0, 8).substr($uuid, 9, 4).substr($uuid, 15, 3));
50+
if ('-' === $time[0]) {
51+
throw new \InvalidArgumentException('Cannot convert UUID to v7: its timestamp is before the Unix epoch.');
52+
}
53+
54+
$ms = \strlen($time) > 4 ? substr($time, 0, -4) : '0';
55+
$time = dechex(10000 * hexdec(substr($uuid, 20, 3)) + substr($time, -4));
56+
57+
if (\strlen($time) > 6) {
58+
$uuid[29] = dechex(hexdec($uuid[29]) ^ hexdec($time[0]));
59+
$time = substr($time, 1);
60+
}
4961

50-
return new self(substr($uuidV1, 15, 3).substr($uuidV1, 9, 4).$uuidV1[0].'-'.substr($uuidV1, 1, 4).'-6'.substr($uuidV1, 5, 3).substr($uuidV1, 18, 6).substr($uuidV1, 24));
62+
return new UuidV7(substr_replace(sprintf(
63+
'%012s-7%s-%s%s-%s%06s',
64+
\PHP_INT_SIZE >= 8 ? dechex($ms) : bin2hex(BinaryUtil::fromBase($ms, BinaryUtil::BASE10)),
65+
substr($uuid, -6, 3),
66+
$uuid[19],
67+
substr($uuid, -3),
68+
substr($uuid, -12, 6),
69+
$time
70+
), '-', 8, 0));
5171
}
5272

5373
public static function generate(\DateTimeInterface $time = null, Uuid $node = null): string

src/Symfony/Component/Uid/UuidV7.php

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -49,28 +49,6 @@ public function getDateTime(): \DateTimeImmutable
4949
return \DateTimeImmutable::createFromFormat('U.v', substr_replace($time, '.', -3, 0));
5050
}
5151

52-
/**
53-
* Sub-millisecond timestamp precision is lost since UUIDv7 don't support it.
54-
*/
55-
public static function fromV1(UuidV1 $uuidV1): self
56-
{
57-
$uuidV1 = $uuidV1->toRfc4122();
58-
$time = '0'.substr($uuidV1, 15, 3).substr($uuidV1, 9, 4).substr($uuidV1, 0, 8);
59-
$time = BinaryUtil::hexToNumericString($time);
60-
if ('-' === $time[0]) {
61-
throw new \InvalidArgumentException('UUIDv1 with a timestamp before Unix epoch cannot be converted to UUIDv7');
62-
}
63-
64-
$time = str_pad($time, 5, '0', \STR_PAD_LEFT);
65-
66-
return new self(substr_replace(sprintf(
67-
'%012s-7%03s-%s',
68-
\PHP_INT_SIZE >= 8 ? dechex(substr($time, 0, -4)) : bin2hex(BinaryUtil::fromBase(substr($time, 0, -4), BinaryUtil::BASE10)),
69-
dechex((int) substr($time, -4, 3)),
70-
substr($uuidV1, 19)
71-
), '-', 8, 0));
72-
}
73-
7452
public static function generate(\DateTimeInterface $time = null): string
7553
{
7654
if (null === $mtime = $time) {

0 commit comments

Comments
 (0)
0