生成v4 UUID的PHP函数

311

所以我一直在探索如何在PHP中生成有效的v4 UUID函数。这是我能做到的最接近的方法。但是,我对于十六进制、十进制、二进制、PHP位运算符等方面的知识几乎没有。这个函数可以生成一个有效的v4 UUID,但有一个问题。v4 UUID应该是这种形式:

xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx

其中y应该为8、9、A或B。但这个函数没有遵守这个规则。

我希望有更多相关知识的人可以帮我解决这个问题。

以下是该函数:

<?php

function gen_uuid() {
 $uuid = array(
  'time_low'  => 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;
}

?>

11
如果您使用Linux,并且有点懒,您可以通过$newId = exec('uuidgen -r');来生成它们。 - JorgeGarza
你可以考虑使用这个库:https://github.com/abmmhasan/UUID 然后只需使用以下命令:\AbmmHasan\Uuid::v4(); - abmmhasan
19个回答

5

用于PHP >= 7的加密安全的UUID v4。

<?php

function uuid4() {
    /* 32 random HEX + space for 4 hyphens */
    $out = bin2hex(random_bytes(18));

    $out[8]  = "-";
    $out[13] = "-";
    $out[18] = "-";
    $out[23] = "-";

    /* UUID v4 */
    $out[14] = "4";
    
    /* variant 1 - 10xx */
    $out[19] = ["8", "9", "a", "b"][random_int(0, 3)];

    return $out;
}

echo uuid4();

输出:c68469d2-065b-4b17-b36f-5c40efb5f6cd


4

我曾经搜索过同样的内容,并且几乎自己实现了一个版本,所以我认为值得一提的是,如果你是在 WordPress 框架内进行此操作,WordPress 有自己超级方便的函数来完成这个任务:

$myUUID = wp_generate_uuid4();

你可以在这里阅读描述和源代码(链接)


2
WP函数专门使用mt_rand函数,因此可能没有足够的随机性。 - Herbert Peters
@HerbertPeters 你说得对。我之所以提到它,只是因为它只有一行代码。我原本想说,如果能添加一个过滤器来返回更安全/保证随机的数值,那将非常好;但反过来,如果你愿意,你也可以返回“false”。 - indextwo

3

broofa在此处的回答启发。

preg_replace_callback('/[xy]/', function ($matches)
{
  return dechex('x' == $matches[0] ? mt_rand(0, 15) : (mt_rand(0, 15) & 0x3 | 0x8));
}
, 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx');

或者如果无法使用匿名函数。

preg_replace_callback('/[xy]/', create_function(
  '$matches',
  'return dechex("x" == $matches[0] ? mt_rand(0, 15) : (mt_rand(0, 15) & 0x3 | 0x8));'
)
, 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx');

1
如果您查看其他答案中的评论,您会看到人们说mt_rand()不能保证随机性。 - Daniel Cheung

3
使用mysql生成uuid如何?
$conn = new mysqli($servername, $username, $password, $dbname, $port);

$query = 'SELECT UUID()';
echo $conn->query($query)->fetch_row()[0];

9
MySQL的UUID()函数创建v1 uuids。 - staticsan

2
这可以更简单吗?
$uuid = bin2hex(openssl_random_pseudo_bytes(16));
for($cnt = 8; $cnt <=23; $cnt+=5)
   $uuid = substr($uuid, 0, $cnt) . "-" . substr($uuid, $cnt);

echo $uuid . "\n";

2
目前你的回答不够清晰,请编辑并添加更多细节以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

2

我相信有更优雅的方法将二进制转换为4xxxyxxx部分的十进制数。但是如果你想要使用openssl_random_pseudo_bytes作为你的加密安全数字生成器,这是我使用的��码:

最初的回答:

return sprintf('%s-%s-%04x-%04x-%s',
    bin2hex(openssl_random_pseudo_bytes(4)),
    bin2hex(openssl_random_pseudo_bytes(2)),
    hexdec(bin2hex(openssl_random_pseudo_bytes(2))) & 0x0fff | 0x4000,
    hexdec(bin2hex(openssl_random_pseudo_bytes(2))) & 0x3fff | 0x8000,
    bin2hex(openssl_random_pseudo_bytes(6))
    );

我认为这是最优雅的解决方案(函数名称很长很丑,但它们就是它们)。 - Max Lumnar

1

来自Tom,在http://www.php.net/manual/zh/function.uniqid.php

$r = unpack('v*', fread(fopen('/dev/random', 'r'),16));
$uuid = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
    $r[1], $r[2], $r[3], $r[4] & 0x0fff | 0x4000,
    $r[5] & 0x3fff | 0x8000, $r[6], $r[7], $r[8])

3
如果他们没有在运行Unix或Linux/GNU系统,那么这段代码就无法工作。 - Cole Tobin
4
如果/dev/random没有足够的熵量来重新加载,这也可能会导致程序运行缓慢。 - ObsidianX
1
/dev/urandom 应该是可以的 - /dev/random 只应用于长期密码密钥的生成。 - Iiridayn
基于此,我想出了这个 - 它使用多种可能的随机源作为后备,并在没有更高级的可用选项时使用mt_rand()进行种子生成。 - mindplay.dk
1
现在,只需在PHP 7中使用random_bytes()即可开始使用 :-) - mindplay.dk

1

uuid_create 函数可以在 PHP 7.2 及更高版本中使用。

      $uuid = uuid_create();
      echo $uuid;

1
在PHP 8.1上,这对我来说不起作用。也许你正在使用其他的库。 - undefined

-1

只是一个想法,但我最终为了获得V4 GUID而做的事情是使用数据库服务器。我正在使用SQL Server,在我需要GUID的场景中,我已经在运行查询,所以我只需将newid()添加为查询结果字段之一。这给了我所需的V4 GUID。

这显然取决于您使用的数据库服务器,以及在需要GUID的代码中发生了什么(以及您需要多少个GUID),但如果您的DB服务器生成v4 GUID,尤其是如果您正在运行查询,那就是一种快速简单且与PHP版本无关的获取GUID的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接