Skip to content

Commit 685c2f3

Browse files
committed
feat: support max UUIDs
1 parent e6164bd commit 685c2f3

18 files changed

+247
-17
lines changed

psalm-baseline.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,9 @@
9696
</MixedArrayAssignment>
9797
</file>
9898
<file src="src/Rfc4122/UuidBuilder.php">
99-
<ImpureVariable occurrences="17">
99+
<ImpureVariable occurrences="19">
100+
<code>$this</code>
101+
<code>$this</code>
100102
<code>$this</code>
101103
<code>$this</code>
102104
<code>$this</code>

src/Guid/Fields.php

+11-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Ramsey\Uuid\Exception\InvalidArgumentException;
1818
use Ramsey\Uuid\Fields\SerializableFieldsTrait;
1919
use Ramsey\Uuid\Rfc4122\FieldsInterface;
20+
use Ramsey\Uuid\Rfc4122\MaxTrait;
2021
use Ramsey\Uuid\Rfc4122\NilTrait;
2122
use Ramsey\Uuid\Rfc4122\VariantTrait;
2223
use Ramsey\Uuid\Rfc4122\VersionTrait;
@@ -44,6 +45,7 @@
4445
*/
4546
final class Fields implements FieldsInterface
4647
{
48+
use MaxTrait;
4749
use NilTrait;
4850
use SerializableFieldsTrait;
4951
use VariantTrait;
@@ -149,7 +151,13 @@ public function getTimestamp(): Hexadecimal
149151

150152
public function getClockSeq(): Hexadecimal
151153
{
152-
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
154+
if ($this->isMax()) {
155+
$clockSeq = 0xffff;
156+
} elseif ($this->isNil()) {
157+
$clockSeq = 0x0000;
158+
} else {
159+
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
160+
}
153161

154162
return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
155163
}
@@ -171,7 +179,7 @@ public function getNode(): Hexadecimal
171179

172180
public function getVersion(): ?int
173181
{
174-
if ($this->isNil()) {
182+
if ($this->isNil() || $this->isMax()) {
175183
return null;
176184
}
177185

@@ -183,7 +191,7 @@ public function getVersion(): ?int
183191

184192
private function isCorrectVariant(): bool
185193
{
186-
if ($this->isNil()) {
194+
if ($this->isNil() || $this->isMax()) {
187195
return true;
188196
}
189197

src/Nonstandard/Fields.php

+5
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,9 @@ public function isNil(): bool
130130
{
131131
return false;
132132
}
133+
134+
public function isMax(): bool
135+
{
136+
return false;
137+
}
133138
}

src/Rfc4122/Fields.php

+10-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
*/
4141
final class Fields implements FieldsInterface
4242
{
43+
use MaxTrait;
4344
use NilTrait;
4445
use SerializableFieldsTrait;
4546
use VariantTrait;
@@ -88,7 +89,13 @@ public function getBytes(): string
8889

8990
public function getClockSeq(): Hexadecimal
9091
{
91-
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
92+
if ($this->isMax()) {
93+
$clockSeq = 0xffff;
94+
} elseif ($this->isNil()) {
95+
$clockSeq = 0x0000;
96+
} else {
97+
$clockSeq = hexdec(bin2hex(substr($this->bytes, 8, 2))) & 0x3fff;
98+
}
9299

93100
return new Hexadecimal(str_pad(dechex($clockSeq), 4, '0', STR_PAD_LEFT));
94101
}
@@ -184,7 +191,7 @@ public function getTimestamp(): Hexadecimal
184191

185192
public function getVersion(): ?int
186193
{
187-
if ($this->isNil()) {
194+
if ($this->isNil() || $this->isMax()) {
188195
return null;
189196
}
190197

@@ -196,7 +203,7 @@ public function getVersion(): ?int
196203

197204
private function isCorrectVariant(): bool
198205
{
199-
if ($this->isNil()) {
206+
if ($this->isNil() || $this->isMax()) {
200207
return true;
201208
}
202209

src/Rfc4122/MaxTrait.php

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the ramsey/uuid library
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*
9+
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
10+
* @license http://opensource.org/licenses/MIT MIT
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Ramsey\Uuid\Rfc4122;
16+
17+
/**
18+
* Provides common functionality for max UUIDs
19+
*
20+
* The max UUID is special form of UUID that is specified to have all 128 bits
21+
* set to one. It is the inverse of the nil UUID.
22+
*
23+
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4 Max UUID
24+
*
25+
* @psalm-immutable
26+
*/
27+
trait MaxTrait
28+
{
29+
/**
30+
* Returns the bytes that comprise the fields
31+
*/
32+
abstract public function getBytes(): string;
33+
34+
/**
35+
* Returns true if the byte string represents a max UUID
36+
*/
37+
public function isMax(): bool
38+
{
39+
return $this->getBytes() === "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
40+
}
41+
}

src/Rfc4122/MaxUuid.php

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/**
4+
* This file is part of the ramsey/uuid library
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*
9+
* @copyright Copyright (c) Ben Ramsey <ben@benramsey.com>
10+
* @license http://opensource.org/licenses/MIT MIT
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Ramsey\Uuid\Rfc4122;
16+
17+
use Ramsey\Uuid\Uuid;
18+
19+
/**
20+
* The max UUID is special form of UUID that is specified to have all 128 bits
21+
* set to one
22+
*
23+
* @psalm-immutable
24+
*/
25+
final class MaxUuid extends Uuid implements UuidInterface
26+
{
27+
}

src/Rfc4122/UuidBuilder.php

+5
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,17 @@ public function __construct(
7373
public function build(CodecInterface $codec, string $bytes): UuidInterface
7474
{
7575
try {
76+
/** @var Fields $fields */
7677
$fields = $this->buildFields($bytes);
7778

7879
if ($fields->isNil()) {
7980
return new NilUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
8081
}
8182

83+
if ($fields->isMax()) {
84+
return new MaxUuid($fields, $this->numberConverter, $codec, $this->timeConverter);
85+
}
86+
8287
switch ($fields->getVersion()) {
8388
case Uuid::UUID_TYPE_TIME:
8489
return new UuidV1($fields, $this->numberConverter, $codec, $this->timeConverter);

src/Rfc4122/VariantTrait.php

+6
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ public function getVariant(): int
5858
throw new InvalidBytesException('Invalid number of bytes');
5959
}
6060

61+
if ($this->isMax() || $this->isNil()) {
62+
// RFC 4122 defines these special types of UUID, so we will consider
63+
// them as belonging to the RFC 4122 variant.
64+
return Uuid::RFC_4122;
65+
}
66+
6167
/** @var array $parts */
6268
$parts = unpack('n*', $this->getBytes());
6369

src/Rfc4122/VersionTrait.php

+6-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@ trait VersionTrait
2828
*/
2929
abstract public function getVersion(): ?int;
3030

31+
/**
32+
* Returns true if these fields represent a max UUID
33+
*/
34+
abstract public function isMax(): bool;
35+
3136
/**
3237
* Returns true if these fields represent a nil UUID
3338
*/
@@ -40,7 +45,7 @@ abstract public function isNil(): bool;
4045
*/
4146
private function isCorrectVersion(): bool
4247
{
43-
if ($this->isNil()) {
48+
if ($this->isNil() || $this->isMax()) {
4449
return true;
4550
}
4651

src/Uuid.php

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ class Uuid implements UuidInterface
8484
*/
8585
public const NIL = '00000000-0000-0000-0000-000000000000';
8686

87+
/**
88+
* The max UUID is a special form of UUID that is specified to have all 128
89+
* bits set to one
90+
*
91+
* @link https://datatracker.ietf.org/doc/html/draft-peabody-dispatch-new-uuid-format-04#section-5.4 Max UUID
92+
*/
93+
public const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
94+
8795
/**
8896
* Variant: reserved, NCS backward compatibility
8997
*

tests/ExpectedBehaviorTest.php

+12-3
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ public function provideIsValid()
198198

199199
// Non RFC 4122 UUIDs
200200
['ffffffff-ffff-ffff-ffff-ffffffffffff', true],
201+
['00000000-0000-0000-0000-000000000000', true],
201202
['ff6f8cb0-c57d-01e1-0b21-0800200c9a66', true],
202203
['ff6f8cb0-c57d-01e1-1b21-0800200c9a66', true],
203204
['ff6f8cb0-c57d-01e1-2b21-0800200c9a66', true],
@@ -254,7 +255,12 @@ public function testNumericReturnValues($string)
254255
$value = BigInteger::fromBase($value, 16)->__toString();
255256
});
256257

257-
$clockSeq = (int) $components[3] & 0x3fff;
258+
if (strtolower($string) === Uuid::MAX) {
259+
$clockSeq = (int) $components[3];
260+
} else {
261+
$clockSeq = (int) $components[3] & 0x3fff;
262+
}
263+
258264
$clockSeqHiAndReserved = (int) $components[3] >> 8;
259265
$clockSeqLow = (int) $components[3] & 0x00ff;
260266

@@ -349,7 +355,7 @@ public function testFromString($string, $version, $variant, $integer)
349355
public function provideFromStringInteger()
350356
{
351357
return [
352-
['00000000-0000-0000-0000-000000000000', null, 0, '0'],
358+
['00000000-0000-0000-0000-000000000000', null, 2, '0'],
353359
['ff6f8cb0-c57d-11e1-8b21-0800200c9a66', 1, 2, '339532337419071774304650190139318639206'],
354360
['ff6f8cb0-c57d-11e1-9b21-0800200c9a66', 1, 2, '339532337419071774305803111643925486182'],
355361
['ff6f8cb0-c57d-11e1-ab21-0800200c9a66', 1, 2, '339532337419071774306956033148532333158'],
@@ -382,7 +388,7 @@ public function provideFromStringInteger()
382388
['ff6f8cb0-c57d-01e1-db21-0800200c9a66', null, 6, '339532337419071698752551071748029454950'],
383389
['ff6f8cb0-c57d-01e1-eb21-0800200c9a66', null, 7, '339532337419071698753703993252636301926'],
384390
['ff6f8cb0-c57d-01e1-fb21-0800200c9a66', null, 7, '339532337419071698754856914757243148902'],
385-
['ffffffff-ffff-ffff-ffff-ffffffffffff', null, 7, '340282366920938463463374607431768211455'],
391+
['ffffffff-ffff-ffff-ffff-ffffffffffff', null, 2, '340282366920938463463374607431768211455'],
386392
];
387393
}
388394

@@ -641,6 +647,7 @@ public function provideUuidConstantTests()
641647
['NAMESPACE_OID', '6ba7b812-9dad-11d1-80b4-00c04fd430c8'],
642648
['NAMESPACE_X500', '6ba7b814-9dad-11d1-80b4-00c04fd430c8'],
643649
['NIL', '00000000-0000-0000-0000-000000000000'],
650+
['MAX', 'ffffffff-ffff-ffff-ffff-ffffffffffff'],
644651
['RESERVED_NCS', 0],
645652
['RFC_4122', 2],
646653
['RESERVED_MICROSOFT', 6],
@@ -651,6 +658,8 @@ public function provideUuidConstantTests()
651658
['UUID_TYPE_HASH_MD5', 3],
652659
['UUID_TYPE_RANDOM', 4],
653660
['UUID_TYPE_HASH_SHA1', 5],
661+
['UUID_TYPE_REORDERED_TIME', 6],
662+
['UUID_TYPE_UNIX_TIME', 7],
654663
];
655664
}
656665
}

tests/Guid/FieldsTest.php

+20-1
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public function fieldGetterMethodProvider(): array
139139
['b08c6fff7dc5e111cb210800200c9a66', 'getVariant', 6],
140140
['b08c6fff7dc5e111cb210800200c9a66', 'getVersion', 1],
141141
['b08c6fff7dc5e111cb210800200c9a66', 'isNil', false],
142+
['b08c6fff7dc5e111cb210800200c9a66', 'isMax', false],
142143

143144
// For ff6f8cb0-c57d-41e1-db21-0800200c9a66
144145
['b08c6fff7dc5e141db210800200c9a66', 'getClockSeq', '1b21'],
@@ -152,6 +153,7 @@ public function fieldGetterMethodProvider(): array
152153
['b08c6fff7dc5e141db210800200c9a66', 'getVariant', 6],
153154
['b08c6fff7dc5e141db210800200c9a66', 'getVersion', 4],
154155
['b08c6fff7dc5e141db210800200c9a66', 'isNil', false],
156+
['b08c6fff7dc5e141db210800200c9a66', 'isMax', false],
155157

156158
// For ff6f8cb0-c57d-31e1-8b21-0800200c9a66
157159
['b08c6fff7dc5e1318b210800200c9a66', 'getClockSeq', '0b21'],
@@ -165,6 +167,7 @@ public function fieldGetterMethodProvider(): array
165167
['b08c6fff7dc5e1318b210800200c9a66', 'getVariant', 2],
166168
['b08c6fff7dc5e1318b210800200c9a66', 'getVersion', 3],
167169
['b08c6fff7dc5e1318b210800200c9a66', 'isNil', false],
170+
['b08c6fff7dc5e1318b210800200c9a66', 'isMax', false],
168171

169172
// For ff6f8cb0-c57d-51e1-9b21-0800200c9a66
170173
['b08c6fff7dc5e1519b210800200c9a66', 'getClockSeq', '1b21'],
@@ -178,6 +181,7 @@ public function fieldGetterMethodProvider(): array
178181
['b08c6fff7dc5e1519b210800200c9a66', 'getVariant', 2],
179182
['b08c6fff7dc5e1519b210800200c9a66', 'getVersion', 5],
180183
['b08c6fff7dc5e1519b210800200c9a66', 'isNil', false],
184+
['b08c6fff7dc5e1519b210800200c9a66', 'isMax', false],
181185

182186
// For 00000000-0000-0000-0000-000000000000
183187
['00000000000000000000000000000000', 'getClockSeq', '0000'],
@@ -188,9 +192,24 @@ public function fieldGetterMethodProvider(): array
188192
['00000000000000000000000000000000', 'getTimeLow', '00000000'],
189193
['00000000000000000000000000000000', 'getTimeMid', '0000'],
190194
['00000000000000000000000000000000', 'getTimestamp', '000000000000000'],
191-
['00000000000000000000000000000000', 'getVariant', 0],
195+
['00000000000000000000000000000000', 'getVariant', 2],
192196
['00000000000000000000000000000000', 'getVersion', null],
193197
['00000000000000000000000000000000', 'isNil', true],
198+
['00000000000000000000000000000000', 'isMax', false],
199+
200+
// For ffffffff-ffff-ffff-ffff-ffffffffffff
201+
['ffffffffffffffffffffffffffffffff', 'getClockSeq', 'ffff'],
202+
['ffffffffffffffffffffffffffffffff', 'getClockSeqHiAndReserved', 'ff'],
203+
['ffffffffffffffffffffffffffffffff', 'getClockSeqLow', 'ff'],
204+
['ffffffffffffffffffffffffffffffff', 'getNode', 'ffffffffffff'],
205+
['ffffffffffffffffffffffffffffffff', 'getTimeHiAndVersion', 'ffff'],
206+
['ffffffffffffffffffffffffffffffff', 'getTimeLow', 'ffffffff'],
207+
['ffffffffffffffffffffffffffffffff', 'getTimeMid', 'ffff'],
208+
['ffffffffffffffffffffffffffffffff', 'getTimestamp', 'fffffffffffffff'],
209+
['ffffffffffffffffffffffffffffffff', 'getVariant', 2],
210+
['ffffffffffffffffffffffffffffffff', 'getVersion', null],
211+
['ffffffffffffffffffffffffffffffff', 'isNil', false],
212+
['ffffffffffffffffffffffffffffffff', 'isMax', true],
194213
];
195214
}
196215

tests/Nonstandard/FieldsTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ public function fieldGetterMethodProvider(): array
6363
['ff6f8cb0-c57d-91e1-0b21-0800200c9a66', 'getVariant', Uuid::RESERVED_NCS],
6464
['ff6f8cb0-c57d-91e1-0b21-0800200c9a66', 'getVersion', null],
6565
['ff6f8cb0-c57d-91e1-0b21-0800200c9a66', 'isNil', false],
66+
['ff6f8cb0-c57d-91e1-0b21-0800200c9a66', 'isMax', false],
6667
];
6768
}
6869

0 commit comments

Comments
 (0)