Skip to content

Commit

Permalink
[UUID] Added support for V3 and V5
Browse files Browse the repository at this point in the history
  • Loading branch information
lyrixx committed Mar 3, 2020
1 parent 1554fc4 commit 6acf2ec
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

* added `preg_last_error_msg()` to the PHP 8 polyfill
* added interface `Stringable` to the PHP 8 polyfill
* added support for UUID V3 and V5

# 1.14.0

Expand Down
115 changes: 100 additions & 15 deletions src/Uuid/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,18 @@ final class Uuid
const UUID_VARIANT_OTHER = 3;
const UUID_TYPE_DEFAULT = 0;
const UUID_TYPE_TIME = 1;
const UUID_TYPE_DCE = 4;
const UUID_TYPE_NAME = 1;
const UUID_TYPE_MD5 = 3;
const UUID_TYPE_DCE = 4; // Deprecated alias
const UUID_TYPE_NAME = 1; // Deprecated alias
const UUID_TYPE_RANDOM = 4;
const UUID_TYPE_SHA1 = 5;
const UUID_TYPE_NULL = -1;
const UUID_TYPE_INVALID = -42;

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

return null;
}
Expand All @@ -48,14 +50,97 @@ public static function uuid_create($uuid_type = UUID_TYPE_DEFAULT)
return self::uuid_generate_random();
default:
trigger_error(sprintf("Unknown/invalid UUID type '%d' requested, using default type instead", $uuid_type), E_USER_WARNING);

return self::uuid_generate_random();
}
}

public static function uuid_generate_md5($uuid_ns, $name)
{
if (!\is_string($uuid_ns)) {
trigger_error(sprintf('uuid_generate_md5() expects parameter 1 to be string, %s given', gettype($uuid_ns)), E_USER_WARNING);

return null;
}

if (!\is_string($name)) {
trigger_error(sprintf('uuid_generate_md5() expects parameter 2 to be string, %s given', gettype($name)), E_USER_WARNING);

return null;
}

if (null === self::uuid_parse_as_array($uuid_ns)) {
return false;
}

$ctx = hash_init('md5');
hash_update($ctx, self::uuid_parse($uuid_ns));
hash_update($ctx, $name);
$hash = hash_final($ctx);

return sprintf('%08s-%04s-%04x-%04x-%012s',
// 32 bits for "time_low"
substr($hash, 0, 8),
// 16 bits for "time_mid"
substr($hash, 8, 4),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 3
hexdec(substr($hash, 12, 4)) & 0x0fff | 0x3000,
// 16 bits:
// * 8 bits for "clk_seq_hi_res",
// * 8 bits for "clk_seq_low",
hexdec(substr($hash, 16, 4)) & 0x3fff | 0x8000,
// 48 bits for "node"
substr($hash, 20, 12)
);
}

public static function uuid_generate_sha1($uuid_ns, $name)
{
if (!\is_string($uuid_ns)) {
trigger_error(sprintf('uuid_generate_sha1() expects parameter 1 to be string, %s given', gettype($uuid_ns)), E_USER_WARNING);

return null;
}

if (!\is_string($name)) {
trigger_error(sprintf('uuid_generate_sha1() expects parameter 2 to be string, %s given', gettype($name)), E_USER_WARNING);

return null;
}

if (null === self::uuid_parse_as_array($uuid_ns)) {
return false;
}

$ctx = hash_init('sha1');
hash_update($ctx, self::uuid_parse($uuid_ns));
hash_update($ctx, $name);
$hash = hash_final($ctx);

return sprintf('%08s-%04s-%04x-%04x-%012s',
// 32 bits for "time_low"
substr($hash, 0, 8),
// 16 bits for "time_mid"
substr($hash, 8, 4),
// 16 bits for "time_hi_and_version",
// four most significant bits holds version number 5
hexdec(substr($hash, 12, 4)) & 0x0fff | 0x5000,
// 16 bits:
// * 8 bits for "clk_seq_hi_res",
// * 8 bits for "clk_seq_low",
// WARNING: On old libuuid version, there is a bug. 0x0fff is used instead of 0x3fff
// See https://github.com/karelzak/util-linux/commit/d6ddf07d31dfdc894eb8e7e6842aa856342c526e
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,
// 48 bits for "node"
substr($hash, 20, 12)
);
}

public static function uuid_is_valid($uuid)
{
if (!\is_string($uuid)) {
trigger_error(sprintf("uuid_is_valid() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
trigger_error(sprintf('uuid_is_valid() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);

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

return null;
}

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

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

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

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

return null;
}
Expand Down Expand Up @@ -158,7 +243,7 @@ public static function uuid_variant($uuid)
public static function uuid_time($uuid)
{
if (!\is_string($uuid)) {
trigger_error(sprintf("uuid_time() expects parameter 1 to be string, %s given", gettype($uuid)), E_USER_WARNING);
trigger_error(sprintf('uuid_time() expects parameter 1 to be string, %s given', gettype($uuid)), E_USER_WARNING);

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

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

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

return null;
}
Expand Down Expand Up @@ -342,12 +427,12 @@ private static function uuid_parse_as_array($uuid)
return null;
}

return array(
return [
'time_low' => hexdec($matches['time_low']),
'time_mid' => hexdec($matches['time_mid']),
'time_hi_and_version' => hexdec($matches['time_hi_and_version']),
'clock_seq' => hexdec($matches['clock_seq']),
'node' => hexdec($matches['node']),
);
];
}
}
8 changes: 6 additions & 2 deletions src/Uuid/bootstrap.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@
define('UUID_VARIANT_OTHER', 3);
define('UUID_TYPE_DEFAULT', 0);
define('UUID_TYPE_TIME', 1);
define('UUID_TYPE_DCE', 4);
define('UUID_TYPE_NAME', 1);
define('UUID_TYPE_MD5', 3);
define('UUID_TYPE_DCE', 4); // Deprecated alias
define('UUID_TYPE_NAME', 1); // Deprecated alias
define('UUID_TYPE_RANDOM', 4);
define('UUID_TYPE_SHA1', 5);
define('UUID_TYPE_NULL', -1);
define('UUID_TYPE_INVALID', -42);

function uuid_create($type = UUID_TYPE_DEFAULT) { return p\Uuid::uuid_create($type); }
function uuid_generate_md5($uuid_ns, $name) { return p\Uuid::uuid_generate_md5($uuid_ns, $name); }
function uuid_generate_sha1($uuid_ns, $name) { return p\Uuid::uuid_generate_sha1($uuid_ns, $name); }
function uuid_is_valid($uuid) { return p\Uuid::uuid_is_valid($uuid); }
function uuid_compare($uuid1, $uuid2) { return p\Uuid::uuid_compare($uuid1, $uuid2); }
function uuid_is_null($uuid) { return p\Uuid::uuid_is_null($uuid); }
Expand Down
30 changes: 30 additions & 0 deletions tests/Uuid/UuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,36 @@ public function testCreateTime()
$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));
}

public function testGenerateMd5()
{
$uuidNs = uuid_create();

$this->assertFalse(uuid_generate_md5("not a uuid", "foo"));

$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"));
$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"));
$this->assertNotSame($a, $b);
$this->assertSame(UUID_TYPE_MD5, uuid_type($a));
$this->assertSame(UUID_TYPE_MD5, uuid_type($b));

$this->assertSame('828658e4-5ae7-39fc-820b-d01a789b1a4d', uuid_generate_md5('ec07aa88-f84e-47b9-a581-1c6b30a2f484', 'the name'));
}

public function testGenerateSha1()
{
$uuidNs = uuid_create();

$this->assertFalse(uuid_generate_sha1("not a uuid", "foo"));

$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"));
$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"));
$this->assertNotSame($a, $b);
$this->assertSame(UUID_TYPE_SHA1, uuid_type($a));
$this->assertSame(UUID_TYPE_SHA1, uuid_type($b));

$this->assertSame('851def0c-b9c7-55aa-a991-130e769ec0a9', uuid_generate_sha1('ec07aa88-f84e-47b9-a581-1c6b30a2f484', 'the name'));
}

public function provideCreateNoOverlapTests()
{
return array(
Expand Down

0 comments on commit 6acf2ec

Please sign in to comment.