Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit ea3a194

Browse filesBrowse files
[Uid] add AbstractUid and interop with base-58/32/RFC4122 encodings
1 parent 1d955cc commit ea3a194
Copy full SHA for ea3a194

File tree

Expand file treeCollapse file tree

10 files changed

+213
-71
lines changed
Filter options
Expand file treeCollapse file tree

10 files changed

+213
-71
lines changed
+106Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Uid;
13+
14+
/**
15+
* @experimental in 5.1
16+
*
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*/
19+
abstract class AbstractUid implements \JsonSerializable
20+
{
21+
/**
22+
* The identifier in its canonic representation.
23+
*/
24+
protected $uid;
25+
26+
/**
27+
* Whether the passed value is valid for the constructor of the current class.
28+
*/
29+
abstract public static function isValid(string $uid): bool;
30+
31+
/**
32+
* Creates an AbstractUid from an identifier represented in any of the supported formats.
33+
*
34+
* @return static
35+
*
36+
* @throws \InvalidArgumentException When the passed value is not valid
37+
*/
38+
abstract public static function fromString(string $uid): self;
39+
40+
/**
41+
* Returns the identifier as a raw binary string.
42+
*/
43+
abstract public function toBinary(): string;
44+
45+
/**
46+
* Returns the identifier as a base-58 case sensitive string.
47+
*/
48+
public function toBase58(): string
49+
{
50+
return strtr(sprintf('%022s', BinaryUtil::toBase($this->toBinary(), BinaryUtil::BASE58)), '0', '1');
51+
}
52+
53+
/**
54+
* Returns the identifier as a base-32 case insensitive string.
55+
*/
56+
public function toBase32(): string
57+
{
58+
$uid = bin2hex($this->toBinary());
59+
$uid = sprintf('%02s%04s%04s%04s%04s%04s%04s',
60+
base_convert(substr($uid, 0, 2), 16, 32),
61+
base_convert(substr($uid, 2, 5), 16, 32),
62+
base_convert(substr($uid, 7, 5), 16, 32),
63+
base_convert(substr($uid, 12, 5), 16, 32),
64+
base_convert(substr($uid, 17, 5), 16, 32),
65+
base_convert(substr($uid, 22, 5), 16, 32),
66+
base_convert(substr($uid, 27, 5), 16, 32)
67+
);
68+
69+
return strtr($uid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ');
70+
}
71+
72+
/**
73+
* Returns the identifier as a RFC4122 case insensitive string.
74+
*/
75+
public function toRfc4122(): string
76+
{
77+
return uuid_unparse($this->toBinary());
78+
}
79+
80+
/**
81+
* Returns whether the argument is an AbstractUid and contains the same value as the current instance.
82+
*/
83+
public function equals($other): bool
84+
{
85+
if (!$other instanceof self) {
86+
return false;
87+
}
88+
89+
return $this->uid === $other->uid;
90+
}
91+
92+
public function compare(self $other): int
93+
{
94+
return (\strlen($this->uid) - \strlen($other->uid)) ?: ($this->uid <=> $other->uid);
95+
}
96+
97+
public function __toString(): string
98+
{
99+
return $this->uid;
100+
}
101+
102+
public function jsonSerialize(): string
103+
{
104+
return $this->uid;
105+
}
106+
}

‎src/Symfony/Component/Uid/BinaryUtil.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/BinaryUtil.php
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,19 @@ class BinaryUtil
2323
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
2424
];
2525

26+
public const BASE58 = [
27+
'' => '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz',
28+
1 => 0, 1, 2, 3, 4, 5, 6, 7, 8, 'A' => 9,
29+
'B' => 10, 'C' => 11, 'D' => 12, 'E' => 13, 'F' => 14, 'G' => 15,
30+
'H' => 16, 'J' => 17, 'K' => 18, 'L' => 19, 'M' => 20, 'N' => 21,
31+
'P' => 22, 'Q' => 23, 'R' => 24, 'S' => 25, 'T' => 26, 'U' => 27,
32+
'V' => 28, 'W' => 29, 'X' => 30, 'Y' => 31, 'Z' => 32, 'a' => 33,
33+
'b' => 34, 'c' => 35, 'd' => 36, 'e' => 37, 'f' => 38, 'g' => 39,
34+
'h' => 40, 'i' => 41, 'j' => 42, 'k' => 43, 'm' => 44, 'n' => 45,
35+
'o' => 46, 'p' => 47, 'q' => 48, 'r' => 49, 's' => 50, 't' => 51,
36+
'u' => 52, 'v' => 53, 'w' => 54, 'x' => 55, 'y' => 56, 'z' => 57,
37+
];
38+
2639
public static function toBase(string $bytes, array $map): string
2740
{
2841
$base = \strlen($alphabet = $map['']);

‎src/Symfony/Component/Uid/NilUuid.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/NilUuid.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ class NilUuid extends Uuid
2222

2323
public function __construct()
2424
{
25-
$this->uuid = '00000000-0000-0000-0000-000000000000';
25+
$this->uid = '00000000-0000-0000-0000-000000000000';
2626
}
2727
}

‎src/Symfony/Component/Uid/Tests/UlidTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/Tests/UlidTest.php
+23Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Uid\Ulid;
16+
use Symfony\Component\Uid\UuidV4;
1617

1718
class UlidTest extends TestCase
1819
{
@@ -49,6 +50,28 @@ public function testBinary()
4950
$this->assertTrue($ulid->equals(Ulid::fromString(hex2bin('7fffffffffffffffffffffffffffffff'))));
5051
}
5152

53+
public function testFromUuid()
54+
{
55+
$uuid = new UuidV4();
56+
57+
$ulid = Ulid::fromString($uuid);
58+
59+
$this->assertSame($uuid->toBase32(), (string) $ulid);
60+
$this->assertSame($ulid->toBase32(), (string) $ulid);
61+
$this->assertSame((string) $uuid, $ulid->toRfc4122());
62+
$this->assertTrue($ulid->equals(Ulid::fromString($uuid)));
63+
}
64+
65+
public function testBase58()
66+
{
67+
$ulid = new Ulid('00000000000000000000000000');
68+
$this->assertSame('1111111111111111111111', $ulid->toBase58());
69+
70+
$ulid = Ulid::fromString("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");
71+
$this->assertSame('YcVfxkQb6JRzqk5kF2tNLv', $ulid->toBase58());
72+
$this->assertTrue($ulid->equals(Ulid::fromString('YcVfxkQb6JRzqk5kF2tNLv')));
73+
}
74+
5275
/**
5376
* @group time-sensitive
5477
*/

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/Tests/UuidTest.php
+21Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use PHPUnit\Framework\TestCase;
1515
use Symfony\Component\Uid\NilUuid;
16+
use Symfony\Component\Uid\Ulid;
1617
use Symfony\Component\Uid\Uuid;
1718
use Symfony\Component\Uid\UuidV1;
1819
use Symfony\Component\Uid\UuidV3;
@@ -95,6 +96,26 @@ public function testBinary()
9596
$this->assertSame(self::A_UUID_V4, (string) $uuid);
9697
}
9798

99+
public function testFromUlid()
100+
{
101+
$ulid = new Ulid();
102+
$uuid = Uuid::fromString($ulid);
103+
104+
$this->assertSame((string) $ulid, $uuid->toBase32());
105+
$this->assertSame((string) $uuid, $uuid->toRfc4122());
106+
$this->assertTrue($uuid->equals(Uuid::fromString($ulid)));
107+
}
108+
109+
public function testBase58()
110+
{
111+
$uuid = new NilUuid();
112+
$this->assertSame('1111111111111111111111', $uuid->toBase58());
113+
114+
$uuid = Uuid::fromString("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");
115+
$this->assertSame('YcVfxkQb6JRzqk5kF2tNLv', $uuid->toBase58());
116+
$this->assertTrue($uuid->equals(Uuid::fromString('YcVfxkQb6JRzqk5kF2tNLv')));
117+
}
118+
98119
public function testIsValid()
99120
{
100121
$this->assertFalse(Uuid::isValid('not a uuid'));

‎src/Symfony/Component/Uid/Ulid.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/Ulid.php
+18-33Lines changed: 18 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,15 @@
2020
*
2121
* @author Nicolas Grekas <p@tchwork.com>
2222
*/
23-
class Ulid implements \JsonSerializable
23+
class Ulid extends AbstractUid
2424
{
2525
private static $time = -1;
2626
private static $rand = [];
2727

28-
private $ulid;
29-
3028
public function __construct(string $ulid = null)
3129
{
3230
if (null === $ulid) {
33-
$this->ulid = self::generate();
31+
$this->uid = self::generate();
3432

3533
return;
3634
}
@@ -39,7 +37,7 @@ public function __construct(string $ulid = null)
3937
throw new \InvalidArgumentException(sprintf('Invalid ULID: "%s".', $ulid));
4038
}
4139

42-
$this->ulid = strtr($ulid, 'abcdefghjkmnpqrstvwxyz', 'ABCDEFGHJKMNPQRSTVWXYZ');
40+
$this->uid = strtr($ulid, 'abcdefghjkmnpqrstvwxyz', 'ABCDEFGHJKMNPQRSTVWXYZ');
4341
}
4442

4543
public static function isValid(string $ulid): bool
@@ -55,8 +53,17 @@ public static function isValid(string $ulid): bool
5553
return $ulid[0] <= '7';
5654
}
5755

58-
public static function fromString(string $ulid): self
56+
/**
57+
* {@inheritdoc}
58+
*/
59+
public static function fromString(string $ulid): parent
5960
{
61+
if (36 === \strlen($ulid) && Uuid::isValid($ulid)) {
62+
$ulid = Uuid::fromString($ulid)->toBinary();
63+
} elseif (22 === \strlen($ulid) && 22 === strspn($ulid, BinaryUtil::BASE58[''])) {
64+
$ulid = BinaryUtil::fromBase($ulid, BinaryUtil::BASE58);
65+
}
66+
6067
if (16 !== \strlen($ulid)) {
6168
return new static($ulid);
6269
}
@@ -75,9 +82,9 @@ public static function fromString(string $ulid): self
7582
return new self(strtr($ulid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ'));
7683
}
7784

78-
public function toBinary()
85+
public function toBinary(): string
7986
{
80-
$ulid = strtr($this->ulid, 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
87+
$ulid = strtr($this->uid, 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
8188

8289
$ulid = sprintf('%02s%05s%05s%05s%05s%05s%05s',
8390
base_convert(substr($ulid, 0, 2), 32, 16),
@@ -92,26 +99,14 @@ public function toBinary()
9299
return hex2bin($ulid);
93100
}
94101

95-
/**
96-
* Returns whether the argument is of class Ulid and contains the same value as the current instance.
97-
*/
98-
public function equals($other): bool
102+
public function toBase32(): string
99103
{
100-
if (!$other instanceof self) {
101-
return false;
102-
}
103-
104-
return $this->ulid === $other->ulid;
105-
}
106-
107-
public function compare(self $other): int
108-
{
109-
return $this->ulid <=> $other->ulid;
104+
return $this->uid;
110105
}
111106

112107
public function getTime(): float
113108
{
114-
$time = strtr(substr($this->ulid, 0, 10), 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
109+
$time = strtr(substr($this->uid, 0, 10), 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
115110

116111
if (\PHP_INT_SIZE >= 8) {
117112
return hexdec(base_convert($time, 32, 16)) / 1000;
@@ -126,16 +121,6 @@ public function getTime(): float
126121
return BinaryUtil::toBase(hex2bin($time), BinaryUtil::BASE10) / 1000;
127122
}
128123

129-
public function __toString(): string
130-
{
131-
return $this->ulid;
132-
}
133-
134-
public function jsonSerialize(): string
135-
{
136-
return $this->ulid;
137-
}
138-
139124
private static function generate(): string
140125
{
141126
$time = microtime(false);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.