Я тут немного разбирался и пытался собрать воедино функцию, которая генерирует правильный UUID v4 в PHP. Мои познания в шестнадцатеричной, десятичной, двоичной системах счисления, побитовых операторах PHP и тому подобном практически отсутствуют. Эта функция генерирует действительный v4 UUID для некоторого поля. UUID v4 должен иметь вид, как я понимаю:
Функция выглядит следующим образом:
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;
}
?>
Ответ 1
Взято из руководства по PHP, вы можете использовать следующий код:
function gen_uuid() {
return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
// 32 бита для "time_low"
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
// 16 бит для "time_mid"
mt_rand( 0, 0xffff ),
// 16 бит для "time_hi_and_version",
// четыре старших бита содержат номера старших бит
mt_rand( 0, 0x0fff ) | 0x4000,
// 16 бит, 8 бит для "clk_seq_hi_res",
// 8 бит для "clk_seq_low",
// четыре старших бита содержат номер версии // два старших бита содержат ноль и единицу для варианта DCE1.14
mt_rand( 0, 0x3fff ) | 0x8000,
// 48 bits для "узла"
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
);
}
Ответ 2
Вместо того чтобы разбивать это на отдельные поля, проще сгенерировать случайный блок данных и изменить отдельные позиции байтов. Вам также следует использовать генератор случайных чисел вместо функции mt_rand().
Согласно RFC 4122 – раздел 4.4, необходимо изменить эти поля:
time_hi_and_version (биты 4-7 7-го октета),
clock_seq_hi_and_reserved (биты 6 и 7 9-го октета).
Все остальные 122 бита должны быть достаточно случайными.
Следующий подход генерирует 128 бит случайных данных с использованием openssl_random_pseudo_bytes(), которые производят перестановки в октетах, а затем используют bin2hex()и vsprintf() для окончательного форматирования.
function guidv4($data) {
assert(strlen($data) == 16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // установка версии 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // установка бит 6-7 до 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);
// ...
}
Ответ 3
Небольшая вариация, добавляющая поддержку PHP < 7:
// Получение глобального уникального идентификатора в соответствии с RFC-4122
function get_guid() {
$data = PHP_MAJOR_VERSION < 7 ? openssl_random_pseudo_bytes(16) : random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // установка версии to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // биты 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
Ответ 4
Мой ответ основан на функции openssl_random_pseudo_bytes для генерации случайной строки вместо чтения из /dev/urandom:
function guid() {
$randomString = openssl_random_pseudo_bytes(16);
$time_low = bin2hex(substr($randomString, 0, 4));
$time_mid = bin2hex(substr($randomString, 4, 2));
$time_hi_and_version = bin2hex(substr($randomString, 6, 2));
$clock_seq_hi_and_reserved = bin2hex(substr($randomString, 8, 2));
$node = bin2hex(substr($randomString, 10, 6));
/**
* Установите четыре старших бита (биты с 12 по 15) поля
* поля time_hi_and_version в 4-битный номер версии из
* Раздел 4.1.3.
* @see http://tools.ietf.org/html/rfc4122#section-4.1.3
*/
$time_hi_and_version = hexdec($time_hi_and_version);
$time_hi_and_version = $time_hi_and_version >> 4;
$time_hi_and_version = $time_hi_and_version | 0x4000;
/**
* Установите два старших бита (биты 6 и 7) параметров
* clock_seq_hi_and_reserved в ноль и единицу, соответственно.
*/
$clock_seq_hi_and_reserved = hexdec($clock_seq_hi_and_reserved);
$clock_seq_hi_and_reserved = $clock_seq_hi_and_reserved >> 2;
$clock_seq_hi_and_reserved = $clock_seq_hi_and_reserved | 0x8000;
return sprintf('%08s-%04s-%04x-%04x-%012s', $time_low, $time_mid, $time_hi_and_version, $clock_seq_hi_and_reserved, $node);
} // guid
Web