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 eb851a5

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

File tree

9 files changed

+210
-68
lines changed
Filter options

9 files changed

+210
-68
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/NullUuid.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Uid/NullUuid.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ class NullUuid 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\NullUuid;
16+
use Symfony\Component\Uid\Ulid;
1617
use Symfony\Component\Uid\Uuid;
1718
use Symfony\Component\Uid\UuidV1;
1819
use Symfony\Component\Uid\UuidV3;
@@ -82,6 +83,26 @@ public function testBinary()
8283
$this->assertSame(self::A_UUID_V4, (string) $uuid);
8384
}
8485

86+
public function testFromUlid()
87+
{
88+
$ulid = new Ulid();
89+
$uuid = Uuid::fromString($ulid);
90+
91+
$this->assertSame((string) $ulid, $uuid->toBase32());
92+
$this->assertSame((string) $uuid, $uuid->toRfc4122());
93+
$this->assertTrue($uuid->equals(Uuid::fromString($ulid)));
94+
}
95+
96+
public function testBase58()
97+
{
98+
$uuid = new NullUuid();
99+
$this->assertSame('1111111111111111111111', $uuid->toBase58());
100+
101+
$uuid = Uuid::fromString("\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF");
102+
$this->assertSame('YcVfxkQb6JRzqk5kF2tNLv', $uuid->toBase58());
103+
$this->assertTrue($uuid->equals(Uuid::fromString('YcVfxkQb6JRzqk5kF2tNLv')));
104+
}
105+
85106
public function testIsValid()
86107
{
87108
$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
@@ -18,17 +18,15 @@
1818
*
1919
* @author Nicolas Grekas <p@tchwork.com>
2020
*/
21-
class Ulid implements \JsonSerializable
21+
class Ulid extends AbstractUid
2222
{
2323
private static $time = -1;
2424
private static $rand = [];
2525

26-
private $ulid;
27-
2826
public function __construct(string $ulid = null)
2927
{
3028
if (null === $ulid) {
31-
$this->ulid = self::generate();
29+
$this->uid = self::generate();
3230

3331
return;
3432
}
@@ -37,7 +35,7 @@ public function __construct(string $ulid = null)
3735
throw new \InvalidArgumentException(sprintf('Invalid ULID: "%s".', $ulid));
3836
}
3937

40-
$this->ulid = strtr($ulid, 'abcdefghjkmnpqrstvwxyz', 'ABCDEFGHJKMNPQRSTVWXYZ');
38+
$this->uid = strtr($ulid, 'abcdefghjkmnpqrstvwxyz', 'ABCDEFGHJKMNPQRSTVWXYZ');
4139
}
4240

4341
public static function isValid(string $ulid): bool
@@ -53,8 +51,17 @@ public static function isValid(string $ulid): bool
5351
return $ulid[0] <= '7';
5452
}
5553

56-
public static function fromString(string $ulid): self
54+
/**
55+
* {@inheritdoc}
56+
*/
57+
public static function fromString(string $ulid): parent
5758
{
59+
if (36 === \strlen($ulid) && Uuid::isValid($ulid)) {
60+
$ulid = Uuid::fromString($ulid)->toBinary();
61+
} elseif (22 === \strlen($ulid) && 22 === strspn($ulid, BinaryUtil::BASE58[''])) {
62+
$ulid = BinaryUtil::fromBase($ulid, BinaryUtil::BASE58);
63+
}
64+
5865
if (16 !== \strlen($ulid)) {
5966
return new static($ulid);
6067
}
@@ -73,9 +80,9 @@ public static function fromString(string $ulid): self
7380
return new self(strtr($ulid, 'abcdefghijklmnopqrstuv', 'ABCDEFGHJKMNPQRSTVWXYZ'));
7481
}
7582

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

8087
$ulid = sprintf('%02s%05s%05s%05s%05s%05s%05s',
8188
base_convert(substr($ulid, 0, 2), 32, 16),
@@ -90,26 +97,14 @@ public function toBinary()
9097
return hex2bin($ulid);
9198
}
9299

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

110105
public function getTime(): float
111106
{
112-
$time = strtr(substr($this->ulid, 0, 10), 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
107+
$time = strtr(substr($this->uid, 0, 10), 'ABCDEFGHJKMNPQRSTVWXYZ', 'abcdefghijklmnopqrstuv');
113108

114109
if (\PHP_INT_SIZE >= 8) {
115110
return hexdec(base_convert($time, 32, 16)) / 1000;
@@ -124,16 +119,6 @@ public function getTime(): float
124119
return BinaryUtil::toBase(hex2bin($time), BinaryUtil::BASE10) / 1000;
125120
}
126121

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

0 commit comments

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