Skip to content

Commit 4fffe31

Browse files
[Uid] Add support for binary, base-32 and base-58 representations in Uuid::isValid()
1 parent 031dde7 commit 4fffe31

File tree

3 files changed

+77
-17
lines changed

3 files changed

+77
-17
lines changed

src/Symfony/Component/Uid/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
---
66

77
* Make `AbstractUid` implement `Ds\Hashable` if available
8+
* Add support for binary, base-32 and base-58 representations in `Uuid::isValid()`
89

910
7.1
1011
---

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

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,38 @@ public function testIsValid()
204204
$this->assertTrue(UuidV4::isValid(self::A_UUID_V4));
205205
}
206206

207+
public function testIsValidWithVariousFormat()
208+
{
209+
$uuid = Uuid::v4();
210+
211+
$this->assertTrue(Uuid::isValid($uuid->toBase32(), Uuid::FORMAT_BASE_32));
212+
$this->assertFalse(Uuid::isValid($uuid->toBase58(), Uuid::FORMAT_BASE_32));
213+
$this->assertFalse(Uuid::isValid($uuid->toBinary(), Uuid::FORMAT_BASE_32));
214+
$this->assertFalse(Uuid::isValid($uuid->toRfc4122(), Uuid::FORMAT_BASE_32));
215+
216+
$this->assertFalse(Uuid::isValid($uuid->toBase32(), Uuid::FORMAT_BASE_58));
217+
$this->assertTrue(Uuid::isValid($uuid->toBase58(), Uuid::FORMAT_BASE_58));
218+
$this->assertFalse(Uuid::isValid($uuid->toBinary(), Uuid::FORMAT_BASE_58));
219+
$this->assertFalse(Uuid::isValid($uuid->toRfc4122(), Uuid::FORMAT_BASE_58));
220+
221+
$this->assertFalse(Uuid::isValid($uuid->toBase32(), Uuid::FORMAT_BINARY));
222+
$this->assertFalse(Uuid::isValid($uuid->toBase58(), Uuid::FORMAT_BINARY));
223+
$this->assertTrue(Uuid::isValid($uuid->toBinary(), Uuid::FORMAT_BINARY));
224+
$this->assertFalse(Uuid::isValid($uuid->toRfc4122(), Uuid::FORMAT_BINARY));
225+
226+
$this->assertFalse(Uuid::isValid($uuid->toBase32(), Uuid::FORMAT_RFC_4122));
227+
$this->assertFalse(Uuid::isValid($uuid->toBase58(), Uuid::FORMAT_RFC_4122));
228+
$this->assertFalse(Uuid::isValid($uuid->toBinary(), Uuid::FORMAT_RFC_4122));
229+
$this->assertTrue(Uuid::isValid($uuid->toRfc4122(), Uuid::FORMAT_RFC_4122));
230+
231+
$this->assertTrue(Uuid::isValid($uuid->toBase32(), Uuid::FORMAT_ALL));
232+
$this->assertTrue(Uuid::isValid($uuid->toBase58(), Uuid::FORMAT_ALL));
233+
$this->assertTrue(Uuid::isValid($uuid->toBinary(), Uuid::FORMAT_ALL));
234+
$this->assertTrue(Uuid::isValid($uuid->toRfc4122(), Uuid::FORMAT_ALL));
235+
236+
$this->assertFalse(Uuid::isValid('30J7CNpDMfXPZrCsn4Cgey', Uuid::FORMAT_BASE_58), 'Fake base-58 string with the "O" forbidden char is not valid');
237+
}
238+
207239
public function testIsValidWithNilUuid()
208240
{
209241
$this->assertTrue(Uuid::isValid('00000000-0000-0000-0000-000000000000'));

src/Symfony/Component/Uid/Uuid.php

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ class Uuid extends AbstractUid
2727
protected const NIL = '00000000-0000-0000-0000-000000000000';
2828
protected const MAX = 'ffffffff-ffff-ffff-ffff-ffffffffffff';
2929

30+
public const FORMAT_BINARY = 1;
31+
public const FORMAT_BASE_32 = 1 << 1;
32+
public const FORMAT_BASE_58 = 1 << 2;
33+
public const FORMAT_RFC_4122 = 1 << 3;
34+
public const FORMAT_ALL = self::FORMAT_BINARY | self::FORMAT_BASE_32 | self::FORMAT_BASE_58 | self::FORMAT_RFC_4122;
35+
3036
public function __construct(string $uuid, bool $checkVariant = false)
3137
{
3238
$type = preg_match('{^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$}Di', $uuid) ? (int) $uuid[14] : false;
@@ -44,22 +50,7 @@ public function __construct(string $uuid, bool $checkVariant = false)
4450

4551
public static function fromString(string $uuid): static
4652
{
47-
if (22 === \strlen($uuid) && 22 === strspn($uuid, BinaryUtil::BASE58[''])) {
48-
$uuid = str_pad(BinaryUtil::fromBase($uuid, BinaryUtil::BASE58), 16, "\0", \STR_PAD_LEFT);
49-
}
50-
51-
if (16 === \strlen($uuid)) {
52-
// don't use uuid_unparse(), it's slower
53-
$uuid = bin2hex($uuid);
54-
$uuid = substr_replace($uuid, '-', 8, 0);
55-
$uuid = substr_replace($uuid, '-', 13, 0);
56-
$uuid = substr_replace($uuid, '-', 18, 0);
57-
$uuid = substr_replace($uuid, '-', 23, 0);
58-
} elseif (26 === \strlen($uuid) && Ulid::isValid($uuid)) {
59-
$ulid = new NilUlid();
60-
$ulid->uid = strtoupper($uuid);
61-
$uuid = $ulid->toRfc4122();
62-
}
53+
$uuid = self::transformToRfc4122($uuid, self::FORMAT_ALL);
6354

6455
if (__CLASS__ !== static::class || 36 !== \strlen($uuid)) {
6556
return new static($uuid);
@@ -130,8 +121,14 @@ final public static function v8(string $uuid): UuidV8
130121
return new UuidV8($uuid);
131122
}
132123

133-
public static function isValid(string $uuid): bool
124+
public static function isValid(string $uuid, int $format = self::FORMAT_RFC_4122): bool
134125
{
126+
if (36 === \strlen($uuid) && !($format & self::FORMAT_RFC_4122)) {
127+
return false;
128+
}
129+
130+
$uuid = self::transformToRfc4122($uuid, $format);
131+
135132
if (self::NIL === $uuid && \in_array(static::class, [__CLASS__, NilUuid::class], true)) {
136133
return true;
137134
}
@@ -182,4 +179,34 @@ private static function format(string $uuid, string $version): string
182179

183180
return substr_replace($uuid, '-', 23, 0);
184181
}
182+
183+
/**
184+
* Transforms a binary string, a base-32 string or a base-58 string to a RFC4122 string.
185+
*
186+
* @return non-empty-string
187+
*/
188+
private static function transformToRfc4122(string $uuid, int $format = self::FORMAT_RFC_4122): string
189+
{
190+
$fromBase58 = false;
191+
if (22 === \strlen($uuid) && 22 === strspn($uuid, BinaryUtil::BASE58['']) && $format & self::FORMAT_BASE_58) {
192+
$uuid = str_pad(BinaryUtil::fromBase($uuid, BinaryUtil::BASE58), 16, "\0", \STR_PAD_LEFT);
193+
$fromBase58 = true;
194+
}
195+
196+
// base-58 are always transformed to binary string, but they must only be valid when the format is FORMAT_BASE_58
197+
if (16 === \strlen($uuid) && $format & self::FORMAT_BINARY || $fromBase58 && $format & self::FORMAT_BASE_58) {
198+
// don't use uuid_unparse(), it's slower
199+
$uuid = bin2hex($uuid);
200+
$uuid = substr_replace($uuid, '-', 8, 0);
201+
$uuid = substr_replace($uuid, '-', 13, 0);
202+
$uuid = substr_replace($uuid, '-', 18, 0);
203+
$uuid = substr_replace($uuid, '-', 23, 0);
204+
} elseif (26 === \strlen($uuid) && Ulid::isValid($uuid) && $format & self::FORMAT_BASE_32) {
205+
$ulid = new NilUlid();
206+
$ulid->uid = strtoupper($uuid);
207+
$uuid = $ulid->toRfc4122();
208+
}
209+
210+
return $uuid;
211+
}
185212
}

0 commit comments

Comments
 (0)