Web

Функция PHP для генерации UUID v4

Я тут немного разбирался и пытался собрать воедино функцию, которая генерирует правильный 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, необходимо изменить эти поля:

  1. time_hi_and_version (биты 4-7 7-го октета),

  2. 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

Схожие статьи

Контейнеры Flex: центрирование по вертикали и растягивание размеров
Web

Контейнеры Flex: центрирование по вертикали и растягивание размеров

Web

Преобразование строки JSON в SELECT

Web

Множественные выходы из функции

Web

Почему происходит «Неопределенная переменная/индекс/смещение» в PHP