PHP-Funktion zum Generieren der v4-UUID

233

Also habe ich ein bisschen herumgegraben und versucht, eine Funktion zusammenzusetzen, die eine gültige v4-UUID in PHP generiert. Dies ist der nächste, den ich kommen konnte. Mein Wissen über hexadezimale, dezimale, binäre, bitweise PHP-Operatoren und dergleichen ist nahezu nicht vorhanden. Diese Funktion generiert eine gültige v4-UUID bis zu einem Bereich. Eine v4-UUID sollte folgende Form haben:

xxxxxxxx-xxxx- 4 xxx- y xxx-xxxxxxxxxxxx

Dabei ist y 8, 9, A oder B. Hier schlagen die Funktionen fehl, da sie sich nicht daran halten.

Ich hatte gehofft, jemand mit mehr Wissen als ich in diesem Bereich könnte mir helfen und mir helfen, diese Funktion zu reparieren, damit sie dieser Regel entspricht.

Die Funktion ist wie folgt:

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

?>

Vielen Dank an alle, die mir helfen können.

anomareh
quelle
5
Wenn Sie unter Linux sind und ein wenig faul sind, können Sie sie mit$newId = exec('uuidgen -r');
JorgeGarza

Antworten:

282

Aus diesem Kommentar im PHP-Handbuch können Sie Folgendes verwenden:

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 )
    );
}
Wilhelm
quelle
43
Diese Funktion wird Duplikate erstellen , so dass es vermeiden , wenn Sie eindeutige Werte benötigen. Beachten Sie, dass mt_rand () immer dieselbe Folge von Zufallszahlen mit demselben Startwert erzeugt. Jedes Mal, wenn ein Startwert wiederholt wird, wird dieselbe exakte UUID generiert. Um dies zu umgehen, müssten Sie es mit Zeit und Mac-Adresse festlegen, aber ich bin mir nicht sicher, wie Sie dies tun würden, da mt_srand () eine Ganzzahl erfordert.
Pavle Predic
12
@PavlePredic mt_srand (crc32 (serialize ([microtime (true), 'USER_IP', 'ETC'])); (Ich bin ein anderer Wiliam: P)
Wiliam
13
In den PHP-Dokumenten wird ausdrücklich darauf hingewiesen, dass mt_rand () keine kryptografisch sicheren Werte generiert. Mit anderen Worten können von dieser Funktion erzeugte Werte vorhersehbar sein. Wenn Sie sicherstellen möchten, dass die UUIDs nicht vorhersehbar sind, sollten Sie lieber die folgende Jack-Lösung verwenden, die die Funktion openssl_random_pseudo_bytes () verwendet.
Richard Keller
7
Was um alles in der Welt bringt es, eine UUID zu generieren, wenn Sie jedes Feld mit Müll füllen?
Eevee
1
PHP 7.0+ definiert die Funktion random_bytes (), die immer kryptografisch sichere Zufallsbytes generiert oder eine Ausnahme auslöst, wenn dies nicht möglich ist. Dies ist besser als sogar openssl_random_psuedo_bytes (), dessen Ausgabe unter bestimmten Umständen manchmal nicht kryptografisch sicher ist.
Thomasrutter
365

Anstatt es in einzelne Felder zu unterteilen, ist es einfacher, einen zufälligen Datenblock zu generieren und die einzelnen Bytepositionen zu ändern. Sie sollten auch einen besseren Zufallszahlengenerator als mt_rand () verwenden.

Gemäß RFC 4122 - Abschnitt 4.4 müssen Sie diese Felder ändern:

  1. time_hi_and_version (Bits 4-7 des 7. Oktetts),
  2. clock_seq_hi_and_reserved (Bit 6 & 7 des 9. Oktetts)

Alle anderen 122 Bits sollten ausreichend zufällig sein.

Der folgende Ansatz generiert 128 Bit zufälliger Daten unter Verwendung openssl_random_pseudo_bytes(), führt die Permutationen für die Oktette durch und verwendet bin2hex()und führt vsprintf()dann die endgültige Formatierung durch.

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

Mit PHP 7 ist das Generieren von zufälligen Byte-Sequenzen noch einfacher, wenn random_bytes():

function guidv4($data = null)
{
    $data = $data ?? random_bytes(16);
    // ...
}
Jack
quelle
9
Eine Alternative für * nix Benutzer, die nicht die openssl-Erweiterung haben:$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
Iiridayn
5
Außerdem würde ich OpenSSL viel mehr vertrauen als mt_rand.
Prof. Falken Vertrag verletzt
3
@BrunoAugusto ist zufällig und es ist äußerst unwahrscheinlich (mit einer guten zufälligen Quelle), dass Duplikate erhalten werden, aber es ist eine gute Praxis, dies auf Datenbankebene durchzusetzen.
Ja͢ck
9
Gibt es einen Grund, den Aufruf random_bytes (16) NICHT in die Funktion guidv4 einzufügen und daher keinen Parameter an guidv4 übergeben zu müssen?
Stephen R
7
Kleine Verbesserung: Legen Sie einen NULL-Standardwert für $ data fest. Die erste Zeile der Funktion lautet dann: $data = $data ?? random_bytes( 16 ); Jetzt können Sie Ihre eigene zufällige Datenquelle angeben oder die Funktion dies für Sie tun lassen. :-)
Stephen R
118

Wenn Sie Composer- Abhängigkeiten verwenden, sollten Sie diese Bibliothek in Betracht ziehen: https://github.com/ramsey/uuid

Einfacher geht es nicht:

Uuid::uuid4();
djule5
quelle
31
Oh, ich weiß nicht ... Fünf Codezeilen im Vergleich zum Laden einer Bibliothek mit Abhängigkeiten? Ich bevorzuge Jacks Funktion. YMMV
Stephen R
7
+1 an Stephen. Ramsey uuid bietet viel mehr Funktionen als nur uuid4. Ich will keine Banane! Hier hast du den ganzen Dschungel!
lcjury
26
UUIDs sind nicht nur zufällige Zeichenfolgen. Es gibt eine Spezifikation, wie es funktioniert. Um eine richtige zufällige UUID zu generieren, die ich später nicht ablehnen muss, verwende ich lieber eine getestete Bibliothek, als meine eigene Implementierung zu rollen.
Brandon
3
Es ist eine UUIDv4. Es ist (meistens, aber für ein paar Bits) zufällig. Das ist keine Kryptographie. Paranoia gegen "Rolling Your Own" ist dumm.
Gordon
23

Verwenden Sie auf Unix-Systemen den Systemkern, um eine UUID für Sie zu generieren.

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

Credit Samveen unter https://serverfault.com/a/529319/210994

Hinweis!: Wenn Sie diese Methode verwenden, um eine UUID zu erhalten, wird der Entropiepool tatsächlich sehr schnell erschöpft! Ich würde es vermeiden, dies dort zu verwenden, wo es häufig aufgerufen wird.

ThorSummoner
quelle
2
Beachten Sie neben der Portabilität, dass die Zufallsquelle /dev/randomblockiert, wenn der Entropiepool erschöpft ist.
Ja͢ck
@Jack Würden Sie bitte eine Dokumentation zum Thema Entropie-Pool-Erschöpfung auf Unix-Systemen verlinken? Es würde mich interessieren, mehr über einen realistischen Anwendungsfall zu erfahren, bei dem diese Methode zusammenbricht.
ThorSummoner
Ich konnte keine Informationen zum Erstellen dieser speziellen Kernel- /dev/urandomDateiquelle finden, die meines Wissens nicht erschöpfen würden, aber die Gefahr birgen, doppelte Benutzeroberflächen zurückzugeben. Ich denke, es ist ein Kompromiss; Benötigen Sie wirklich eine eindeutige ID, die von der Systementropie beeinflusst wird?
ThorSummoner
13

Bei meiner Suche nach einer v4-UUID bin ich zuerst auf diese Seite gekommen und habe diese dann auf http://php.net/manual/en/function.com-create-guid.php gefunden

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

Kredit: pavel.volyntsev

Bearbeiten: Zur Verdeutlichung erhalten Sie mit dieser Funktion immer eine v4-UUID (PHP> = 5.3.0).

Wenn die Funktion com_create_guid verfügbar ist (normalerweise nur unter Windows), wird sie verwendet und die geschweiften Klammern entfernt.

Wenn es nicht vorhanden ist (Linux), greift es auf diese starke zufällige Funktion openssl_random_pseudo_bytes zurück und formatiert sie dann mit vsprintf in v4 uuid.

Arie
quelle
5

Meine Antwort basiert auf einem Kommentar uniqid Benutzerkommentar , verwendet jedoch die Funktion openssl_random_pseudo_bytes , um eine zufällige Zeichenfolge zu generieren, anstatt aus zu lesen/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
Victor Smirnov
quelle
5

Wenn Sie verwenden CakePHP, können Sie ihre Methode CakeText::uuid();aus der CakeText- Klasse verwenden, um eine RFC4122- UUID zu generieren.

bish
quelle
5

Eine leichte Variation von Jacks Antwort , um Unterstützung für PHP <7 hinzuzufügen:

// 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));
}
Danny Beckett
quelle
4

Inspiriert von Broofas Antwort hier .

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

Oder wenn anonyme Funktionen nicht verwendet werden können.

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');
Michael Rushton
quelle
1
Wenn Sie sich die Kommentare in anderen Antworten ansehen, werden Sie feststellen, dass die Leute sagen mt_rand(), dass Zufälligkeit nicht garantiert ist.
Daniel Cheung
3

Nachdem ich genau das Gleiche gesucht und fast selbst eine Version davon implementiert hatte, fand ich es erwähnenswert, dass WP , wenn Sie dies in einem WordPress- Framework tun , eine eigene superhandliche Funktion für genau dies hat:

$myUUID = wp_generate_uuid4();

Die Beschreibung und die Quelle können Sie hier lesen .

indextwo
quelle
1
Die WP-Funktion verwendet ausschließlich mt_rand. Vielleicht nicht genug Zufälligkeit
Herbert Peters
@HerbertPeters Du hast recht. Ich habe es nur erwähnt, weil es ein Einzeiler ist. Ich wollte sagen, dass es ordentlich wäre, wenn sie einen Filter dafür hinzugefügt hätten, damit Sie eine sicherere / garantierte Zufallszahl zurückgeben könnten. Aber die Kehrseite davon ist, dass Sie, wenn Sie so geneigt wären, auch false🤷
indextwo
2

Wie wäre es mit MySQL, um die UUID für Sie zu generieren?

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

$query = 'SELECT UUID()';
echo $conn->query($query)->fetch_row()[0];
Hoan Dang
quelle
2
Die UUID()Funktion von MySQL erstellt v1-Benutzeroberflächen.
Staticsan
2
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));
Cristián Carrasco
quelle
2
Bitte fügen Sie Ihrem Code eine Erklärung hinzu, damit andere verstehen, was er tut.
KFoobar
Dies ist, was Symfony Polyfil tatsächlich getan hat - github.com/symfony/polyfill-uuid/blob/master/Uuid.php#L320
Serhii Polishchuk
1

Von Tom auf http://www.php.net/manual/en/function.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])
amgine
quelle
3
Was ist, wenn sie nicht Unix oder Linux / GNU ausführen? Dieser Code funktioniert nicht.
Cole Johnson
4
Dies kann auch sehr langsam laufen, wenn / dev / random leer ist und darauf wartet, dass mehr Entropie neu geladen wird.
ObsidianX
1
/dev/urandomsollte in Ordnung sein - /dev/randomsollte nur zur Generierung von langfristigen kryptografischen Schlüsseln verwendet werden.
Iiridayn
Auf dieser Grundlage kam ich mit bis diese - es nutzt mehrere mögliche Quellen der Zufälligkeit als Fall Rücken und Resorts Aussaat , mt_rand()wenn nichts schicker verfügbar ist.
mindplay.dk
1
Inzwischen einfach random_bytes()in PHP 7 verwenden und los geht's :-)
mindplay.dk
1

Ich bin sicher, es gibt eine elegantere Möglichkeit, die Konvertierung von binär in dezimal für die Teile 4xxxund yxxxdurchzuführen. Aber wenn Sie openssl_random_pseudo_bytesals Ihren krytographisch sicheren Zahlengenerator verwenden möchten, verwende ich Folgendes:

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