Skip to content

Commit 4d0b530

Browse files
committed
feature #236 [UUID] Added support for V3 and V5 (lyrixx)
This PR was merged into the 1.14-dev branch. Discussion ---------- [UUID] Added support for V3 and V5 Note: on old libuuid version, the V5 format is broken. Ubuntu 18.04 is affected. This has been fixed [here](util-linux/util-linux@d6ddf07). You can test your distribution with: ``` uuidgen -s -n ec07aa88-f84e-47b9-a581-1c6b30a2f484 -N "the name" ``` You must got: ```bash 851def0c-b9c7-55aa-a991-130e769ec0a9 ``` Some tests: ``` // python: 851def0c-b9c7-55aa-a991-130e769ec0a9 // go: 851def0c-b9c7-55aa-a991-130e769ec0a9 // ramsey: 851def0c-b9c7-55aa-a991-130e769ec0a9 // sf: 851def0c-b9c7-55aa-a991-130e769ec0a9 // libuuid 19.10: 851def0c-b9c7-55aa-a991-130e769ec0a9 // libbuuid arch: 851def0c-b9c7-55aa-a991-130e769ec0a9 // libuuid 18.04: 851def0c-b9c7-55aa-8991-130e769ec0a9 // pecl 18.0.4: 851def0c-b9c7-55aa-8991-130e769ec0a9 ``` Commits ------- 6acf2ec [UUID] Added support for V3 and V5
2 parents 1554fc4 + 6acf2ec commit 4d0b530

File tree

4 files changed

+137
-17
lines changed

4 files changed

+137
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* added `preg_last_error_msg()` to the PHP 8 polyfill
44
* added interface `Stringable` to the PHP 8 polyfill
5+
* added support for UUID V3 and V5
56

67
# 1.14.0
78

src/Uuid/Uuid.php

Lines changed: 100 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,18 @@ final class Uuid
2424
const UUID_VARIANT_OTHER = 3;
2525
const UUID_TYPE_DEFAULT = 0;
2626
const UUID_TYPE_TIME = 1;
27-
const UUID_TYPE_DCE = 4;
28-
const UUID_TYPE_NAME = 1;
27+
const UUID_TYPE_MD5 = 3;
28+
const UUID_TYPE_DCE = 4; // Deprecated alias
29+
const UUID_TYPE_NAME = 1; // Deprecated alias
2930
const UUID_TYPE_RANDOM = 4;
31+
const UUID_TYPE_SHA1 = 5;
3032
const UUID_TYPE_NULL = -1;
3133
const UUID_TYPE_INVALID = -42;
3234

3335
public static function uuid_create($uuid_type = UUID_TYPE_DEFAULT)
3436
{
3537
if (!\is_int($uuid_type)) {
36-
trigger_error(sprintf("uuid_create() expects parameter 1 to be int, %s given", gettype($uuid_type)), E_USER_WARNING);
38+
trigger_error(sprintf('uuid_create() expects parameter 1 to be int, %s given', gettype($uuid_type)), E_USER_WARNING);
3739

3840
return null;
3941
}
@@ -48,14 +50,97 @@ public static function uuid_create($uuid_type = UUID_TYPE_DEFAULT)
4850
return self::uuid_generate_random();
4951
default:
5052
trigger_error(sprintf("Unknown/invalid UUID type '%d' requested, using default type instead", $uuid_type), E_USER_WARNING);
53+
5154
return self::uuid_generate_random();
5255
}
5356
}
5457

58+
public static function uuid_generate_md5($uuid_ns, $name)
59+
{
60+
if (!\is_string($uuid_ns)) {
61+
trigger_error(sprintf('uuid_generate_md5() expects parameter 1 to be string, %s given', gettype($uuid_ns)), E_USER_WARNING);
62+
63+
return null;
64+
}
65+
66+
if (!\is_string($name)) {
67+
trigger_error(sprintf('uuid_generate_md5() expects parameter 2 to be string, %s given', gettype($name)), E_USER_WARNING);
68+
69+
return null;
70+
}
71+
72+
if (null === self::uuid_parse_as_array($uuid_ns)) {
73+
return false;
74+
}
75+
76+
$ctx = hash_init('md5');
77+
hash_update($ctx, self::uuid_parse($uuid_ns));
78+
hash_update($ctx, $name);
79+
$hash = hash_final($ctx);
80+
81+
return sprintf('%08s-%04s-%04x-%04x-%012s',
82+
// 32 bits for "time_low"
83+
substr($hash, 0, 8),
84+
// 16 bits for "time_mid"
85+
substr($hash, 8, 4),
86+
// 16 bits for "time_hi_and_version",
87+
// four most significant bits holds version number 3
88+
hexdec(substr($hash, 12, 4)) & 0x0fff | 0x3000,
89+
// 16 bits:
90+
// * 8 bits for "clk_seq_hi_res",
91+
// * 8 bits for "clk_seq_low",
92+
hexdec(substr($hash, 16, 4)) & 0x3fff | 0x8000,
93+
// 48 bits for "node"
94+
substr($hash, 20, 12)
95+
);
96+
}
97+
98+
public static function uuid_generate_sha1($uuid_ns, $name)
99+
{
100+
if (!\is_string($uuid_ns)) {
101+
trigger_error(sprintf('uuid_generate_sha1() expects parameter 1 to be string, %s given', gettype($uuid_ns)), E_USER_WARNING);
102+
103+
return null;
104+
}
105+
106+
if (!\is_string($name)) {
107+
trigger_error(sprintf('uuid_generate_sha1() expects parameter 2 to be string, %s given', gettype($name)), E_USER_WARNING);
108+
109+
return null;
110+
}
111+
112+
if (null === self::uuid_parse_as_array($uuid_ns)) {
113+
return false;
114+
}
115+
116+
$ctx = hash_init('sha1');
117+
hash_update($ctx, self::uuid_parse($uuid_ns));
118+
hash_update($ctx, $name);
119+
$hash = hash_final($ctx);
120+
121+
return sprintf('%08s-%04s-%04x-%04x-%012s',
122+
// 32 bits for "time_low"
123+
substr($hash, 0, 8),
124+
// 16 bits for "time_mid"
125+
substr($hash, 8, 4),
126+
// 16 bits for "time_hi_and_version",
127+
// four most significant bits holds version number 5
128+
hexdec(substr($hash, 12, 4)) & 0x0fff | 0x5000,
129+
// 16 bits:
130+
// * 8 bits for "clk_seq_hi_res",
131+
// * 8 bits for "clk_seq_low",
132+
// WARNING: On old libuuid version, there is a bug. 0x0fff is used instead of 0x3fff
133+
// See https://github.com/karelzak/util-linux/commit/d6ddf07d31dfdc894eb8e7e6842aa856342c526e
134+
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
135+
// 48 bits for "node"
136+
substr($hash, 20, 12)
137+
);
138+
}
139+
55140
public static function uuid_is_valid($uuid)
56141
{
57142
if (!\is_string($uuid)) {
58-
trigger_error(sprintf("uuid_is_valid() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
143+
trigger_error(sprintf('uuid_is_valid() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
59144

60145
return null;
61146
}
@@ -66,13 +151,13 @@ public static function uuid_is_valid($uuid)
66151
public static function uuid_compare($uuid1, $uuid2)
67152
{
68153
if (!\is_string($uuid1)) {
69-
trigger_error(sprintf("uuid_compare() expects parameter 1 to be string, %s given", gettype($uuid1)), E_USER_WARNING);
154+
trigger_error(sprintf('uuid_compare() expects parameter 1 to be string, %s given', gettype($uuid1)), E_USER_WARNING);
70155

71156
return null;
72157
}
73158

74159
if (!\is_string($uuid2)) {
75-
trigger_error(sprintf("uuid_compare() expects parameter 2 to be string, %s given", gettype($uuid2)), E_USER_WARNING);
160+
trigger_error(sprintf('uuid_compare() expects parameter 2 to be string, %s given', gettype($uuid2)), E_USER_WARNING);
76161

77162
return null;
78163
}
@@ -99,7 +184,7 @@ public static function uuid_compare($uuid1, $uuid2)
99184
public static function uuid_is_null($uuid)
100185
{
101186
if (!\is_string($uuid)) {
102-
trigger_error(sprintf("uuid_is_null() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
187+
trigger_error(sprintf('uuid_is_null() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
103188

104189
return null;
105190
}
@@ -110,7 +195,7 @@ public static function uuid_is_null($uuid)
110195
public static function uuid_type($uuid)
111196
{
112197
if (!\is_string($uuid)) {
113-
trigger_error(sprintf("uuid_type() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
198+
trigger_error(sprintf('uuid_type() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
114199

115200
return null;
116201
}
@@ -129,7 +214,7 @@ public static function uuid_type($uuid)
129214
public static function uuid_variant($uuid)
130215
{
131216
if (!\is_string($uuid)) {
132-
trigger_error(sprintf("uuid_variant() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
217+
trigger_error(sprintf('uuid_variant() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
133218

134219
return null;
135220
}
@@ -158,7 +243,7 @@ public static function uuid_variant($uuid)
158243
public static function uuid_time($uuid)
159244
{
160245
if (!\is_string($uuid)) {
161-
trigger_error(sprintf("uuid_time() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
246+
trigger_error(sprintf('uuid_time() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
162247

163248
return null;
164249
}
@@ -185,7 +270,7 @@ public static function uuid_time($uuid)
185270
public static function uuid_mac($uuid)
186271
{
187272
if (!\is_string($uuid)) {
188-
trigger_error(sprintf("uuid_mac() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
273+
trigger_error(sprintf('uuid_mac() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
189274

190275
return null;
191276
}
@@ -204,7 +289,7 @@ public static function uuid_mac($uuid)
204289
public static function uuid_parse($uuid)
205290
{
206291
if (!\is_string($uuid)) {
207-
trigger_error(sprintf("uuid_parse() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
292+
trigger_error(sprintf('uuid_parse() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);
208293

209294
return null;
210295
}
@@ -221,7 +306,7 @@ public static function uuid_parse($uuid)
221306
public static function uuid_unparse($uuidAsBinary)
222307
{
223308
if (!\is_string($uuidAsBinary)) {
224-
trigger_error(sprintf("uuid_unparse() expects parameter 1 to be string, %s given", gettype($uuidAsBinary)), E_USER_WARNING);
309+
trigger_error(sprintf('uuid_unparse() expects parameter 1 to be string, %s given', gettype($uuidAsBinary)), E_USER_WARNING);
225310

226311
return null;
227312
}
@@ -342,12 +427,12 @@ private static function uuid_parse_as_array($uuid)
342427
return null;
343428
}
344429

345-
return array(
430+
return [
346431
'time_low' => hexdec($matches['time_low']),
347432
'time_mid' => hexdec($matches['time_mid']),
348433
'time_hi_and_version' => hexdec($matches['time_hi_and_version']),
349434
'clock_seq' => hexdec($matches['clock_seq']),
350435
'node' => hexdec($matches['node']),
351-
);
436+
];
352437
}
353438
}

src/Uuid/bootstrap.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,17 @@
1818
define('UUID_VARIANT_OTHER', 3);
1919
define('UUID_TYPE_DEFAULT', 0);
2020
define('UUID_TYPE_TIME', 1);
21-
define('UUID_TYPE_DCE', 4);
22-
define('UUID_TYPE_NAME', 1);
21+
define('UUID_TYPE_MD5', 3);
22+
define('UUID_TYPE_DCE', 4); // Deprecated alias
23+
define('UUID_TYPE_NAME', 1); // Deprecated alias
2324
define('UUID_TYPE_RANDOM', 4);
25+
define('UUID_TYPE_SHA1', 5);
2426
define('UUID_TYPE_NULL', -1);
2527
define('UUID_TYPE_INVALID', -42);
2628

2729
function uuid_create($type = UUID_TYPE_DEFAULT) { return p\Uuid::uuid_create($type); }
30+
function uuid_generate_md5($uuid_ns, $name) { return p\Uuid::uuid_generate_md5($uuid_ns, $name); }
31+
function uuid_generate_sha1($uuid_ns, $name) { return p\Uuid::uuid_generate_sha1($uuid_ns, $name); }
2832
function uuid_is_valid($uuid) { return p\Uuid::uuid_is_valid($uuid); }
2933
function uuid_compare($uuid1, $uuid2) { return p\Uuid::uuid_compare($uuid1, $uuid2); }
3034
function uuid_is_null($uuid) { return p\Uuid::uuid_is_null($uuid); }

tests/Uuid/UuidTest.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,36 @@ public function testCreateTime()
2929
$this->assertRegExp('{^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}', uuid_create(UUID_TYPE_TIME));
3030
}
3131

32+
public function testGenerateMd5()
33+
{
34+
$uuidNs = uuid_create();
35+
36+
$this->assertFalse(uuid_generate_md5("not a uuid", "foo"));
37+
38+
$this->assertRegExp('{^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}', $a = uuid_generate_md5($uuidNs, "foo"));
39+
$this->assertRegExp('{^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}', $b = uuid_generate_md5($uuidNs, "bar"));
40+
$this->assertNotSame($a, $b);
41+
$this->assertSame(UUID_TYPE_MD5, uuid_type($a));
42+
$this->assertSame(UUID_TYPE_MD5, uuid_type($b));
43+
44+
$this->assertSame('828658e4-5ae7-39fc-820b-d01a789b1a4d', uuid_generate_md5('ec07aa88-f84e-47b9-a581-1c6b30a2f484', 'the name'));
45+
}
46+
47+
public function testGenerateSha1()
48+
{
49+
$uuidNs = uuid_create();
50+
51+
$this->assertFalse(uuid_generate_sha1("not a uuid", "foo"));
52+
53+
$this->assertRegExp('{^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}', $a = uuid_generate_sha1($uuidNs, "foo"));
54+
$this->assertRegExp('{^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$}', $b = uuid_generate_sha1($uuidNs, "bar"));
55+
$this->assertNotSame($a, $b);
56+
$this->assertSame(UUID_TYPE_SHA1, uuid_type($a));
57+
$this->assertSame(UUID_TYPE_SHA1, uuid_type($b));
58+
59+
$this->assertSame('851def0c-b9c7-55aa-a991-130e769ec0a9', uuid_generate_sha1('ec07aa88-f84e-47b9-a581-1c6b30a2f484', 'the name'));
60+
}
61+
3262
public function provideCreateNoOverlapTests()
3363
{
3464
return array(

0 commit comments

Comments
 (0)