Skip to content

Commit

Permalink
[Uid] Fix validating UUID variant bits
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolas-grekas committed Sep 8, 2022
1 parent 2120eba commit d35d402
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 13 deletions.
26 changes: 26 additions & 0 deletions Tests/UuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,32 @@ public function provideInvalidUuids(): iterable
yield ['these are just thirty-six characters'];
}

/**
* @dataProvider provideInvalidVariant
*/
public function testInvalidVariant(string $uuid)
{
$uuid = new Uuid($uuid);
$this->assertFalse(Uuid::isValid($uuid));

$uuid = (string) $uuid;
$class = Uuid::class.'V'.$uuid[14];

$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid UUIDv'.$uuid[14].': "'.$uuid.'".');

new $class($uuid);
}

public function provideInvalidVariant(): iterable
{
yield ['8dac64d3-937a-1e7c-fa1d-d5d6c06a61f5'];
yield ['8dac64d3-937a-3e7c-fa1d-d5d6c06a61f5'];
yield ['8dac64d3-937a-4e7c-fa1d-d5d6c06a61f5'];
yield ['8dac64d3-937a-5e7c-fa1d-d5d6c06a61f5'];
yield ['8dac64d3-937a-6e7c-fa1d-d5d6c06a61f5'];
}

public function testConstructorWithValidUuid()
{
$uuid = new UuidV4(self::A_UUID_V4);
Expand Down
4 changes: 2 additions & 2 deletions Ulid.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public static function isValid(string $ulid): bool
*/
public static function fromString(string $ulid): parent
{
if (36 === \strlen($ulid) && Uuid::isValid($ulid)) {
$ulid = (new Uuid($ulid))->toBinary();
if (36 === \strlen($ulid) && preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $ulid)) {
$ulid = uuid_parse($ulid);
} elseif (22 === \strlen($ulid) && 22 === strspn($ulid, BinaryUtil::BASE58[''])) {
$ulid = str_pad(BinaryUtil::fromBase($ulid, BinaryUtil::BASE58), 16, "\0", \STR_PAD_LEFT);
}
Expand Down
22 changes: 14 additions & 8 deletions Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Uuid extends AbstractUid
protected const TYPE = 0;
protected const NIL = '00000000-0000-0000-0000-000000000000';

public function __construct(string $uuid)
public function __construct(string $uuid, bool $checkVariant = false)
{
$type = preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid) ? (int) $uuid[14] : false;

Expand All @@ -35,6 +35,10 @@ public function __construct(string $uuid)
}

$this->uid = strtolower($uuid);

if ($checkVariant && !\in_array($this->uid[19], ['8', '9', 'a', 'b'], true)) {
throw new \InvalidArgumentException(sprintf('Invalid UUID%s: "%s".', static::TYPE ? 'v'.static::TYPE : '', $uuid));
}
}

/**
Expand Down Expand Up @@ -67,12 +71,14 @@ public static function fromString(string $uuid): parent
return new NilUuid();
}

switch ($uuid[14]) {
case UuidV1::TYPE: return new UuidV1($uuid);
case UuidV3::TYPE: return new UuidV3($uuid);
case UuidV4::TYPE: return new UuidV4($uuid);
case UuidV5::TYPE: return new UuidV5($uuid);
case UuidV6::TYPE: return new UuidV6($uuid);
if (\in_array($uuid[19], ['8', '9', 'a', 'b', 'A', 'B'], true)) {
switch ($uuid[14]) {
case UuidV1::TYPE: return new UuidV1($uuid);
case UuidV3::TYPE: return new UuidV3($uuid);
case UuidV4::TYPE: return new UuidV4($uuid);
case UuidV5::TYPE: return new UuidV5($uuid);
case UuidV6::TYPE: return new UuidV6($uuid);
}
}

return new self($uuid);
Expand Down Expand Up @@ -111,7 +117,7 @@ final public static function v6(): UuidV6

public static function isValid(string $uuid): bool
{
if (!preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid)) {
if (!preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){2}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}Di', $uuid)) {
return false;
}

Expand Down
2 changes: 1 addition & 1 deletion UuidV1.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(string $uuid = null)
if (null === $uuid) {
$this->uid = uuid_create(static::TYPE);
} else {
parent::__construct($uuid);
parent::__construct($uuid, true);
}
}

Expand Down
5 changes: 5 additions & 0 deletions UuidV3.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@
class UuidV3 extends Uuid
{
protected const TYPE = 3;

public function __construct(string $uuid)
{
parent::__construct($uuid, true);
}
}
2 changes: 1 addition & 1 deletion UuidV4.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public function __construct(string $uuid = null)

$this->uid = substr($uuid, 0, 8).'-'.substr($uuid, 8, 4).'-'.substr($uuid, 12, 4).'-'.substr($uuid, 16, 4).'-'.substr($uuid, 20, 12);
} else {
parent::__construct($uuid);
parent::__construct($uuid, true);
}
}
}
5 changes: 5 additions & 0 deletions UuidV5.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@
class UuidV5 extends Uuid
{
protected const TYPE = 5;

public function __construct(string $uuid)
{
parent::__construct($uuid, true);
}
}
2 changes: 1 addition & 1 deletion UuidV6.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function __construct(string $uuid = null)
if (null === $uuid) {
$this->uid = static::generate();
} else {
parent::__construct($uuid);
parent::__construct($uuid, true);
}
}

Expand Down

0 comments on commit d35d402

Please sign in to comment.