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

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

Краткая инструкция, как заливать на Github коды собственных программ
Web

Краткая инструкция, как заливать на Github коды собственных программ

Web

Определение размера удаленного файла без загрузки файла

Web

Как восстановить сериализованную строку, которая была повреждена из-за неправильной длины счетчика байтов

Web

Лучшие практики для настраиваемых помощников в Laravel 5