问答中心分类: PHP生成 v4 UUID 的 PHP 函数
0
Victor Smirnov 提问 15分钟 前

所以我一直在做一些挖掘工作,我一直在尝试拼凑一个在 PHP 中生成有效 v4 UUID 的函数。这是我能来的最近的一次。我在十六进制、十进制、二进制、PHP 的位运算符等方面的知识几乎不存在。此函数生成一个有效的 v4 UUID,直到一个区域。 v4 UUID 应采用以下形式:

xxxxxxx-xxxx-4xxx-是的xxx-xxxxxxxxxxxx

在哪里是的是 8、9、A 或 B。这是功能失败的地方,因为它不遵守这一点。
我希望在这方面比我有更多知识的人可以帮助我并帮助我修复此功能,使其符合该规则。
功能如下:

0,
  'time_mid'  => 0,
  'time_hi'  => 0,
  'clock_seq_hi' => 0,
  'clock_seq_low' => 0,
  'node'   => array()
 );
 
 $uuid['time_low'] = mt_rand(0, 0xffff) + (mt_rand(0, 0xffff) << 16);
 $uuid['time_mid'] = mt_rand(0, 0xffff);
 $uuid['time_hi'] = (4 << 12) | (mt_rand(0, 0x1000));
 $uuid['clock_seq_hi'] = (1 << 7) | (mt_rand(0, 128));
 $uuid['clock_seq_low'] = mt_rand(0, 255);
 
 for ($i = 0; $i < 6; $i++) {
  $uuid['node'][$i] = mt_rand(0, 255);
 }
 
 $uuid = sprintf('%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x',
  $uuid['time_low'],
  $uuid['time_mid'],
  $uuid['time_hi'],
  $uuid['clock_seq_hi'],
  $uuid['clock_seq_low'],
  $uuid['node'][0],
  $uuid['node'][1],
  $uuid['node'][2],
  $uuid['node'][3],
  $uuid['node'][4],
  $uuid['node'][5]
 );
 
 return $uuid;
}

?>
JorgeGarza 回复 15分钟 前

如果你在 Linux 上,如果你有点懒惰,你可以用$newId = exec('uuidgen -r');

JorgeGarza 回复 15分钟 前

你可以考虑使用这个库:github.com/abmmhasan/UUID然后只需使用命令:\AbmmHasan\Uuid::v4();

17 Answers
0
Ja͢ck 回答 15分钟 前

与其将其分解为单个字段,不如生成随机数据块并更改单个字节位置更容易。您还应该使用比 mt_rand() 更好的随机数生成器。
根据RFC 4122 – 第 4.4 节,您需要更改这些字段:

  1. time_hi_and_version(第 7 个八位字节的第 4-7 位),
  2. clock_seq_hi_and_reserved(第 9 个八位字节的第 6 位和第 7 位)

所有其他 122 位都应该足够随机。
以下方法使用生成 128 位随机数据openssl_random_pseudo_bytes(), 对八位字节进行排列,然后使用bin2hex()vsprintf()进行最终格式化。

function guidv4($data)
{
    assert(strlen($data) == 16);

    $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10

    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}

echo guidv4(openssl_random_pseudo_bytes(16));

使用 PHP 7,生成随机字节序列更加简单random_bytes()

function guidv4($data = null)
{
    $data = $data ?? random_bytes(16);
    // ...
}
Iiridayn 回复 15分钟 前

没有 openssl 扩展的 *nix 用户的替代方案:$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);

Prof. Falken 回复 15分钟 前

另外,比起 mt_rand,我更信任 OpenSSL。

Bruno Augusto 回复 15分钟 前

多年后,我偶然发现了您的答案,尽管我已经搜索过自己,但我有一个问题:这是随机的还是在实际情况下,我应该在数据库上添加一个额外的验证来处理生成的序列吗?

Ja͢ck 回复 15分钟 前

@BrunoAugusto 它是随机的,并且极不可能(具有良好的随机源)获得重复项,但在数据库级别强制执行它是一种好习惯。

Stephen R 回复 15分钟 前

是否有任何理由不将 random_bytes(16) 调用放在 guidv4 函数中,因此不必将任何参数传递给 guidv4?

Ja͢ck 回复 15分钟 前

@StephenR 它允许调用者选择他们想要如何生成随机数据。

Stephen R 回复 15分钟 前

小改进:给$data设置一个NULL默认值,然后函数的第一行是这样的:$data = $data ?? random_bytes( 16 );现在您可以指定自己的随机数据源,或者让函数为您完成。 🙂

undefined 回复 15分钟 前

@Iiridayn 或uuidgen从 util-linux 2.34

0
djule5 回答 15分钟 前

任何人使用作曲家依赖项,您可能需要考虑这个库:https://github.com/ramsey/uuid
没有比这更容易的了:

Uuid::uuid4();
Stephen R 回复 15分钟 前

哦,我不知道…. 五行代码与加载具有依赖关系的库?我更喜欢杰克的功能。 YMMV

lcjury 回复 15分钟 前

+1 斯蒂芬。 Ramsey uuid 的功能远不止 uuid4。我不要香蕉!这里你有整个丛林!

Brandon 回复 15分钟 前

UUID 不仅仅是随机字符串。它的工作原理有一个规范。为了生成一个合适的随机 UUID,我不必担心以后会被拒绝,我宁愿使用经过测试的库,也不愿推出自己的实现。

Gordon 回复 15分钟 前

这是一个 UUIDv4。它(主要是,但对于少数)是随机的。这不是密码学。反对“自己动手”的偏执狂是愚蠢的。

istepaniuk 回复 15分钟 前

使用库的开销是不存在的,它有测试. +1 不重新发明轮子。

0
ThorSummoner 回答 15分钟 前

在 unix 系统上,使用系统内核为您生成一个 uuid。

file_get_contents('/proc/sys/kernel/random/uuid')

信用萨姆文https://serverfault.com/a/529319/210994

注意!:使用这种方法获取 uuid 实际上会很快耗尽熵池!我会避免在经常调用它的地方使用它。

Ja͢ck 回复 15分钟 前

除了便携性,请注意随机源是/dev/random如果熵池耗尽,则阻塞。

ThorSummoner 回复 15分钟 前

@Jack您能否链接一些有关unix系统上熵池耗尽主题的文档?我有兴趣了解更多关于这种方法失效的实际用例。

ThorSummoner 回复 15分钟 前

我无法找到有关制作此特殊内核文件源的信息/dev/urandom,在我的理解中不会用尽,但有返回重复 uuid 的风险。我想这是一个权衡;你真的需要一个受系统熵影响的唯一ID吗?

ThorSummoner 回复 15分钟 前

我曾经注意到,通过 linux 内核(共享资源)获取 uuid 足以保证同一系统上的唯一 uuid。我相信这个 procfs uuid 以这种方式使用是安全的。请注意,有多个版本的 UUIDen.wikipedia.org/wiki/…和一般的 linux 可能会给你版本 3 和 5 类型man7.org/linux/man-pages/man3/uuid_generate.3.html

jack 回复 15分钟 前

这些解决方案对我来说真的很有趣。好笑!=坏

0
Cristián Carrasco 回答 15分钟 前
// php version >= 7
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));
user5446912 回复 15分钟 前

请为您的代码添加解释,以帮助其他人理解它的作用。

Serhii Polishchuk 回复 15分钟 前

这就是 Symfony polyfil 实际所做的——github.com/symfony/polyfill-uuid/blob/master/Uuid.php#L320

Rafael 回复 15分钟 前

这个对吗?快速测试返回c0a062b7-b225-c294-b8a0-06b98931a45b,与 xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx 不匹配。它返回 ac 而不是 4。

0
Danny Beckett 回答 15分钟 前

略有变化杰克的回答添加对 PHP < 7 的支持:

// Get an RFC-4122 compliant globaly unique identifier
function get_guid() {
    $data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16);
    $data[6] = chr(ord($data[6]) & 0x0f | 0x40);    // Set version to 0100
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80);    // Set bits 6-7 to 10
    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}