Hàm PHP để tạo v4 UUID


233

Vì vậy, tôi đã thực hiện một số hoạt động đào xung quanh và tôi đã cố gắng ghép một hàm tạo ra UUID v4 hợp lệ trong PHP. Đây là lần gần nhất tôi có thể đến. Kiến thức của tôi về các toán tử bit, thập phân, nhị phân, nhị phân của PHP và tương tự gần như không tồn tại. Hàm này tạo UUID v4 hợp lệ cho đến một vùng. Một UUID v4 phải ở dạng:

xxxxxxxx-xxxx- 4 xxx- y xxx-xxxxxxxxxxxx

trong đó y là 8, 9, A hoặc B. Đây là nơi các chức năng thất bại vì nó không tuân thủ điều đó.

Tôi đã hy vọng ai đó có nhiều kiến ​​thức hơn tôi trong lĩnh vực này có thể giúp tôi một tay và giúp tôi sửa chữa chức năng này để nó tuân thủ quy tắc đó.

Chức năng như sau:

<?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;
}

?>

Nhờ bất cứ ai có thể giúp tôi ra.


5
Nếu bạn đang sử dụng Linux và nếu bạn hơi lười biếng, bạn có thể tạo ra chúng với$newId = exec('uuidgen -r');
JorgeGarza

Câu trả lời:


282

Lấy từ nhận xét này trên hướng dẫn sử dụng PHP, bạn có thể sử dụng:

function gen_uuid() {
    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        // 32 bits for "time_low"
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),

        // 16 bits for "time_mid"
        mt_rand( 0, 0xffff ),

        // 16 bits for "time_hi_and_version",
        // four most significant bits holds version number 4
        mt_rand( 0, 0x0fff ) | 0x4000,

        // 16 bits, 8 bits for "clk_seq_hi_res",
        // 8 bits for "clk_seq_low",
        // two most significant bits holds zero and one for variant DCE1.1
        mt_rand( 0, 0x3fff ) | 0x8000,

        // 48 bits for "node"
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff )
    );
}

43
Hàm này sẽ tạo các bản sao, vì vậy hãy tránh nó khi bạn cần các giá trị duy nhất. Lưu ý rằng mt_rand () sẽ luôn tạo ra cùng một chuỗi các số ngẫu nhiên được cung cấp cùng một hạt giống. Vì vậy, mỗi khi một hạt giống được lặp lại, UUID chính xác sẽ được tạo ra. Để giải quyết vấn đề này, bạn sẽ cần chọn nó bằng cách sử dụng thời gian và địa chỉ mac, nhưng tôi không chắc bạn sẽ làm điều này như thế nào, vì mt_srand () yêu cầu một số nguyên.
Pavle Dự đoán

12
@PavlePredic mt_srand (crc32 (tuần tự hóa ([microtime (đúng), 'USER_IP', 'ETC']))); (tôi là một wiliam khác: P)
Wiliam

13
Các tài liệu PHP cảnh báo rõ ràng rằng mt_rand () không tạo ra các giá trị bảo mật bằng mật mã. Nói cách khác, các giá trị được tạo bởi chức năng này có thể dự đoán được. Nếu bạn cần đảm bảo rằng các UUID không thể dự đoán được, bạn nên sử dụng giải pháp của Jack bên dưới, sử dụng hàm openssl_random_pseudo_bytes ().
Richard Keller

7
Điều gì trên trái đất là điểm tạo ra UUID nếu bạn lấp đầy mọi trường với rác?
Eevee

1
PHP 7.0+ định nghĩa hàm Random_bytes () sẽ luôn tạo các byte ngẫu nhiên được bảo mật bằng mật mã hoặc đưa ra một ngoại lệ nếu không thể. Điều này tốt hơn thậm chí openssl_random_psuedo_bytes () có đầu ra đôi khi không được bảo mật bằng mật mã trong một số trường hợp.
thomasrutter

365

Thay vì chia nó thành các trường riêng lẻ, việc tạo một khối dữ liệu ngẫu nhiên và thay đổi các vị trí byte riêng lẻ sẽ dễ dàng hơn. Bạn cũng nên sử dụng trình tạo số ngẫu nhiên tốt hơn mt_rand ().

Theo RFC 4122 - Mục 4.4 , bạn cần thay đổi các trường sau:

  1. time_hi_and_version (bit 4-7 của octet thứ 7),
  2. clock_seq_hi_and_reserved (bit 6 & 7 của octet thứ 9)

Tất cả 122 bit khác phải đủ ngẫu nhiên.

Cách tiếp cận sau đây tạo ra 128 bit dữ liệu ngẫu nhiên bằng cách sử dụng openssl_random_pseudo_bytes(), tạo các hoán vị trên các octet rồi sử dụng bin2hex()vsprintf()thực hiện định dạng cuối cùng.

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));

Với PHP 7, việc tạo các chuỗi byte ngẫu nhiên thậm chí còn đơn giản hơn bằng cách sử dụng random_bytes():

function guidv4($data = null)
{
    $data = $data ?? random_bytes(16);
    // ...
}

9
Một thay thế cho người dùng * nix không có tiện ích mở rộng openssl:$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
Iiridayn

5
Ngoài ra, tôi sẽ tin tưởng OpenSSL hơn rất nhiều so với mt_rand.
Hợp đồng của giáo sư Falken vi phạm

3
@BrunoAugusto là ngẫu nhiên và rất khó xảy ra (với nguồn ngẫu nhiên tốt) để có được các bản sao, nhưng đó là một cách thực hành tốt để thực thi nó ở cấp cơ sở dữ liệu.
Ja͢ck

9
Có bất kỳ lý do nào để KHÔNG đặt lệnh gọi Random_bytes (16) bên trong hàm guidev4 và do đó không phải chuyển bất kỳ tham số nào cho guidev4 không?
Stephen R

7
Cải thiện nhỏ: Đặt mặc định NULL cho $ data và sau đó dòng đầu tiên của hàm là: $data = $data ?? random_bytes( 16 ); Bây giờ bạn CÓ THỂ chỉ định nguồn dữ liệu ngẫu nhiên của riêng mình hoặc để chức năng thực hiện cho bạn. :-)
Stephen R

118

Bất cứ ai sử dụng phụ thuộc nhà soạn nhạc , bạn có thể muốn xem xét thư viện này: https://github.com/ramsey/uuid

Nó không dễ dàng hơn thế này:

Uuid::uuid4();

32
Ồ, tôi không biết .... Năm dòng mã so với tải thư viện có phụ thuộc? Tôi thích chức năng của Jack. YMMV
Stephen R

7
+1 cho Stephen. Ramsey uuid có nhiều chức năng hơn so với chỉ uuid4. Tôi sẽ không phải là một quả chuối! Ở đây bạn có toàn bộ khu rừng!
lcjury

26
UUID không chỉ là chuỗi ngẫu nhiên. Có một thông số kỹ thuật làm thế nào nó hoạt động. Để tạo một UUID ngẫu nhiên thích hợp mà tôi không phải lo lắng về việc bị từ chối sau này, tôi thà sử dụng một thư viện được thử nghiệm hơn là triển khai thực hiện của riêng tôi.
Brandon

3
Đó là một UUIDv4. Đó là (chủ yếu, nhưng cho một vài bit) ngẫu nhiên. Đây không phải là mật mã. Sự hoang tưởng chống lại "lăn của riêng bạn" là ngớ ngẩn.
Gordon

23

trên các hệ thống unix, sử dụng kernel hệ thống để tạo uuid cho bạn.

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

Tín dụng Samveen trên https://serverfault.com/a/529319/210994

Lưu ý!: Sử dụng phương pháp này để có được một uuid trên thực tế làm cạn kiệt hồ bơi entropy, rất nhanh chóng! Tôi sẽ tránh sử dụng cái này ở nơi nó sẽ được gọi thường xuyên.


2
Bên cạnh tính di động, lưu ý rằng nguồn ngẫu nhiên sẽ /dev/randomchặn nếu nhóm entropy cạn kiệt.
Ja͢ck

@Jack Bạn vui lòng liên kết một số tài liệu về chủ đề cạn kiệt entropy pool trên các hệ thống unix xin vui lòng? Tôi muốn biết thêm về trường hợp sử dụng thực tế khi phương pháp này bị hỏng.
ThorSummoner

Tôi không thể tìm thấy thông tin về việc tạo nguồn tập tin hạt nhân đặc biệt này /dev/urandom, theo cách hiểu của tôi sẽ không cạn kiệt, nhưng có nguy cơ trả lại các uuids trùng lặp. Tôi đoán đó là một sự đánh đổi; Bạn có thực sự cần một id duy nhất chịu ảnh hưởng của entropy hệ thống không?
ThorSummoner

13

Trong quá trình tìm kiếm một uuid v4, tôi đã đến trang này trước, sau đó tìm thấy nó trên http://php.net/manual/en/feft.com-create-guid.php

function guidv4()
{
    if (function_exists('com_create_guid') === true)
        return trim(com_create_guid(), '{}');

    $data = openssl_random_pseudo_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));
}

tín dụng: pavel.volyntsev

Chỉnh sửa: để làm rõ, chức năng này sẽ luôn cung cấp cho bạn một v4 uuid (PHP> = 5.3.0).

Khi chức năng com_create_guid khả dụng (thường chỉ có trên Windows), nó sẽ sử dụng chức năng đó và loại bỏ các dấu ngoặc nhọn.

Nếu không có mặt (Linux), nó sẽ rơi vào hàm openssl_random_pseudo_bytes ngẫu nhiên mạnh mẽ này, sau đó nó sẽ sử dụng vsprintf để định dạng nó thành v4 uuid.


5

Câu trả lời của tôi dựa trên nhận xét người dùng uniqid bình luận nhưng nó sử dụng hàm openssl_random_pseudo_bytes để tạo chuỗi ngẫu nhiên thay vì đọc từ/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));

    /**
     * Set the four most significant bits (bits 12 through 15) of the
     * time_hi_and_version field to the 4-bit version number from
     * Section 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;

    /**
     * Set the two most significant bits (bits 6 and 7) of the
     * clock_seq_hi_and_reserved to zero and one, respectively.
     */
    $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

5

Nếu bạn sử dụng, CakePHPbạn có thể sử dụng phương thức của họ CakeText::uuid();từ lớp CakeText để tạo uuid RFC4122.


5

Một thay đổi nhỏ trong câu trả lời của Jack để thêm hỗ trợ cho 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));
}

4

Lấy cảm hứng từ câu trả lời của broofa ở đây .

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');

Hoặc nếu không thể sử dụng chức năng ẩn danh.

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
Nếu bạn nhìn vào các bình luận trong các câu trả lời khác, bạn sẽ thấy mọi người nói mt_rand()không được đảm bảo tính ngẫu nhiên.
Daniel Cheung

3

Đã tìm kiếm chính xác điều tương tự và gần như tự mình thực hiện một phiên bản này, tôi nghĩ rằng điều đáng nói là, nếu bạn đang làm điều này trong khung WordPress , WP có chức năng siêu tiện dụng của riêng nó cho chính xác điều này:

$myUUID = wp_generate_uuid4();

Bạn có thể đọc mô tả và nguồn ở đây .


1
Hàm WP sử dụng độc quyền mt_rand. Vì vậy, có thể không có đủ ngẫu nhiên
Herbert Peters

@HerbertPeter Bạn nói đúng. Tôi chỉ đề cập đến nó bởi vì nó là một lót. Tôi sẽ nói rằng nó sẽ gọn gàng nếu họ đã thêm một bộ lọc cho nó để bạn có thể trả về một số ngẫu nhiên an toàn / bảo đảm hơn; nhưng điểm nổi bật của điều đó là, nếu bạn rất có khuynh hướng, bạn cũng có thể quay lại false🤷
indextwo

2

Làm thế nào về việc sử dụng mysql để tạo uuid cho bạn?

$conn = new mysqli($servername, $username, $password, $dbname, $port);

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

2
UUID()Chức năng của MySQL tạo ra uuids v1.
staticsan


1

Từ tom, trên http://www.php.net/manual/en/feft.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
Nếu họ không chạy Unix hoặc Linux / GNU thì sao? Mã này sẽ không hoạt động.
Cole Johnson

4
Điều này cũng có khả năng chạy rất chậm nếu / dev / ngẫu nhiên trống và đang chờ thêm entropy để tải lại.
ObsidianX

1
/dev/urandomnên ổn - /dev/randomchỉ nên được sử dụng để tạo khóa mật mã dài hạn.
Iiridayn

Dựa vào đó, tôi đã nghĩ ra điều này - nó sử dụng một số nguồn ngẫu nhiên có thể là dự phòng và các khu nghỉ mát để gieo hạt mt_rand()nếu không có gì lạ hơn.
mindplay.dk

1
Đến bây giờ, chỉ cần sử dụng random_bytes()trong PHP 7 và tắt đi :-)
mindplay.dk

1

Tôi chắc chắn có một cách thanh lịch hơn để thực hiện chuyển đổi từ nhị phân sang thập phân cho các phần 4xxxyxxxphần. Nhưng nếu bạn muốn sử dụng openssl_random_pseudo_byteslàm trình tạo số an toàn về mặt bảo mật, đây là những gì tôi sử dụng:

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))
    );

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.