V4 UUID oluşturmak için PHP işlevi


233

Bu yüzden biraz kazma yapıyorum ve PHP'de geçerli bir v4 UUID üreten bir işlevi bir araya getirmeye çalışıyorum. Gelebildiğim en yakın şey bu. Onaltılık, ondalık, ikili, PHP'nin bitsel operatörleri ve benzerleri hakkındaki bilgilerim neredeyse hiç yok. Bu işlev bir alana kadar geçerli bir v4 UUID oluşturur. Bir v4 UUID şu şekilde olmalıdır:

xxxxxxxx-xxxx- 4 xxx- y xxx-xxxxxxxxxxxx

burada y 8, 9, A veya B'dir. Bu, işlevlerin ona uymadığı için başarısız olduğu yerdir.

Bu alanda benden daha fazla bilgiye sahip birisinin bana bir el verebileceğini ve bu kurala uyması için bu işlevi düzeltmeme yardımcı olacağını umuyordum.

Fonksiyon aşağıdaki gibidir:

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

?>

Bana yardım edebilecek herkese teşekkürler.


5
Eğer Linux'taysanız ve biraz tembelseniz, onları üretebilirsiniz$newId = exec('uuidgen -r');
JorgeGarza

Yanıtlar:


282

PHP kılavuzundaki bu yorumdan alındığında, bunu kullanabilirsiniz:

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
Bu fonksiyon olacaktır çiftleri oluşturmak, böylece benzersiz değerler gerektiğinde bunu önlemek. Mt_rand () işlevinin her zaman aynı tohum verildiğinde aynı rasgele sayılar dizisini üreteceğini unutmayın. Böylece her tohum tekrarlandığında aynı UUID üretilir. Bunu aşmak için, zaman ve mac adresini kullanarak onu tohumlamanız gerekir, ancak mt_srand () bir tamsayı gerektirdiğinden bunu nasıl yapacağınızdan emin değilim.
Pavle Predic

12
@PavlePredic mt_srand (crc32 (serileştirme ([mikrotime (gerçek), 'USER_IP', 'VB']))); (Ben başka bir wiliam'ım: P)
Wiliam

13
PHP, mt_rand () 'ın kriptografik olarak güvenli değerler üretmediğini açıkça uyarır. Başka bir deyişle, bu fonksiyon tarafından üretilen değerler tahmin edilebilir. UUID'lerin öngörülebilir olmadığından emin olmanız gerekiyorsa, Jack'in aşağıdaki openssl_random_pseudo_bytes () işlevini kullanan çözümünü kullanmalısınız.
Richard Keller

7
her alanı çöple doldurursanız, bir UUID oluşturmanın anlamı nedir?
Eevee

1
PHP 7.0+ her zaman kriptografik olarak güvenli rastgele baytlar oluşturacak veya yapamazsa istisna atacak random_bytes () işlevini tanımlar. Bu, çıkışı bazı durumlarda kriptografik olarak güvenli olmayan openssl_random_psuedo_bytes () bile daha iyidir.
thomasrutter

365

Tek tek alanlara ayırmak yerine, rastgele bir veri bloğu oluşturmak ve tek tek bayt konumlarını değiştirmek daha kolaydır. Ayrıca mt_rand () 'dan daha iyi bir rasgele sayı üreteci kullanmalısınız.

Göre RFC 4122 - Bölüm 4.4 , bu alanları değiştirmek gerekir:

  1. time_hi_and_version (7. sekizlinin 4-7 bitleri),
  2. clock_seq_hi_and_reserved (9. oktet'in 6. ve 7. bitleri)

Diğer 122 bitin tümü yeterince rastgele olmalıdır.

Aşağıdaki yaklaşım rastgele veriler 128 bit kullanarak üretir openssl_random_pseudo_bytes(), sekizli üzerinde permütasyon yapar ve kullanır bin2hex()vevsprintf() ve son biçimlendirmeyi yapmak için .

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

PHP 7 ile rastgele bayt dizileri oluşturmak daha da basittir random_bytes():

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

9
$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
Openssl

5
Ayrıca, OpenSSL'ye mt_rand'dan çok daha fazla güvenirim.
Prof. Falken

3
@BrunoAugusto rastgele ve yinelenen (iyi bir rastgele kaynak ile) son derece olası değil, ancak veritabanı düzeyinde zorlamak için iyi bir uygulamadır.
Ja͢ck

9
Random_bytes (16) çağrısını guidv4 fonksiyonunun içine KOYMAMAK için herhangi bir sebep var mı ve bu nedenle guidv4'e herhangi bir parametre iletmek zorunda değil misiniz?
Stephen R

7
Küçük iyileştirme: $ data için bir NULL varsayılan ayarlayın ve sonra işlevin ilk satırı şudur: $data = $data ?? random_bytes( 16 ); Artık kendi rastgele veri kaynağınızı belirtebilir veya işlevin sizin için yapmasına izin verebilirsiniz. :-)
Stephen R

118

Besteci bağımlılığı kullanan herkes bu kütüphaneyi düşünebilirsiniz: https://github.com/ramsey/uuid

Bundan daha kolay olamaz:

Uuid::uuid4();

32
Oh, bilmiyorum .... Beş kod satırı vs bağımlılık içeren bir kütüphane yükleme? Jack'in işlevini tercih ederim. YMMV
Stephen R

7
Stephen'a +1. Ramsey uuid sadece uuid4'ten çok daha fazla işlevselliğe sahiptir. Muz istemiyorum !, burada tüm orman var!
lcjury

26
UUID'ler sadece rastgele dizeler değildir. Nasıl çalıştığına dair bir spesifikasyon var. Daha sonra reddetme konusunda endişelenmem gereken uygun bir rastgele UUID oluşturmak için, kendi uygulamamı yuvarlamak yerine test edilmiş bir kütüphane kullanmayı tercih ederim.
Brandon

3
Bu bir UUIDv4. Bu (çoğunlukla, ama birkaç bit için) rastgele. Bu bir şifreleme değil. Paranoyaya karşı "kendiniz haddeleme" aptalca.
Gordon

23

unix sistemlerde, sizin için bir uuid oluşturmak için sistem çekirdeğini kullanın.

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

Kredi Samveen üzerinde https://serverfault.com/a/529319/210994

Not !: Bir uuid almak için bu yöntemi kullanmak aslında entropi havuzunu çok hızlı bir şekilde tüketir! Ben sık sık çağrılacak yerde kullanmaktan kaçınır.


2
Taşınabilirliğin yanı sıra /dev/random, entropi havuzunun tükenmesi durumunda rastgele kaynağın engellendiğini unutmayın .
Ja͢ck

@Jack Lütfen unix sistemlerinde entropi havuzunun tükenmesi ile ilgili bazı belgeleri bağlar mısınız? Bu yöntemin bozulduğu gerçekçi bir kullanım durumu hakkında daha fazla bilgi edinmek isterim.
ThorSummoner

Bu özel çekirdek dosya kaynağını yapma konusunda bilgi bulamadım /dev/urandom, ki bu benim anlayışımla yorulmaz, ancak yinelenen uuidleri geri döndürme riskleri. Sanırım bu bir takas; gerçekten sistem entropisinden etkilenen benzersiz bir kimliğe ihtiyacınız var mı?
ThorSummoner

13

Bir v4 uuid oluşturma arayışımda, önce bu sayfaya geldim, sonra http://php.net/manual/en/function.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));
}

Kredi bilgileri: pavel.volyntsev

Düzenleme: açıklığa kavuşturmak için, bu işlev her zaman size bir v4 uuid (PHP> = 5.3.0) verecektir.

Com_create_guid işlevi kullanılabilir olduğunda (genellikle yalnızca Windows'ta), bunu kullanır ve süslü parantezleri sıyırır.

Mevcut değilse (Linux), bu güçlü rastgele openssl_random_pseudo_bytes fonksiyonuna geri döner, daha sonra vsprintf'yi v4 uuid olarak biçimlendirmek için kullanır.


5

Cevabım yorum uniqid kullanıcı yorum dayanmaktadır, ancak okuma yerine rastgele dize oluşturmak için openssl_random_pseudo_bytes işlevini kullanır/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

Eğer kullanırsanız CakePHPonların yöntemi kullanabilirsiniz CakeText::uuid();gelen CakeText bir RFC4122 uuid oluşturmak için sınıfa.


5

PHP <7 için destek eklemek için Jack'in cevabındaki küçük bir değişiklik :

// 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

Esinlenerek broofa 'ın cevabı burada .

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

Veya anonim işlevleri kullanamıyorsanız.

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
Diğer cevaplardaki yorumlara bakarsanız, insanların mt_rand()rastgelelik garantisi olmadığını söylerdiniz .
Daniel Cheung

3

Tam olarak aynı şeyi araştırıp neredeyse kendimin bir sürümünü uyguladıktan sonra, bir WordPress çerçevesi içinde yapıyorsanız , WP'nin tam olarak bunun için kendi süper kullanışlı işlevine sahip olduğunu belirtmeye değer olduğunu düşündüm :

$myUUID = wp_generate_uuid4();

Açıklamayı ve kaynağı buradan okuyabilirsiniz .


1
WP İşlevi yalnızca mt_rand kullanır. Yeterince rasgele olmayabilir
Herbert Peters

@HerbertPeters Haklısın. Sadece bahsetmiştim, çünkü bu tek astarlı. Ben daha güvenli / garantili rasgele bir sayı dönebilir böylece bunun için bir filtre eklemiş olsaydı temiz olacağını söyleyecektim; ama bunun tersi, eğer bu kadar eğimli olsaydınız, false🤷
indextwo,

2

Sizin için uuid oluşturmak için mysql kullanmaya ne dersiniz?

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

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

2
MySQL'in UUID()işlevi v1 uuids oluşturur.
staticsan


1

Tom'dan, http://www.php.net/manual/en/function.uniqid.php adresinde

$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
Ya Unix veya Linux / GNU çalıştırmıyorlarsa? Bu kod çalışmaz.
Cole Johnson

4
/ Dev / random boşsa ve daha fazla entropinin yeniden yüklenmesini bekliyorsa, bu çok yavaş çalışma potansiyeline sahiptir.
ObsidianX

1
/dev/urandomiyi /dev/randomolmalı - yalnızca uzun vadeli kriptografik anahtarların oluşturulması için kullanılmalıdır.
Iiridayn

Buna dayanarak, ben ile geldi bu - bu tohumlama için birkaç olası sonbahar almalar gibi rastgelelik kaynakları ve tatil kullanır mt_rand()şey meraklısı varsa.
mindplay.dk

1
Şimdiye kadar, random_bytes()PHP 7'de kullanın ve gidin :-)
mindplay.dk

1

Eminim ikili bölümlerden ondalığa dönüşüm 4xxxve yxxxbölümler için daha zarif bir yol vardır . Ama eğer openssl_random_pseudo_byteskritografik olarak güvenli sayı üreteciniz olarak kullanmak istiyorsanız , bunu kullanıyorum:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.