v4 UUIDを生成するPHP関数


233

そのため、私はいくつかの調査を行っており、PHPで有効なv4 UUIDを生成する関数をつなぎ合わせようとしています。これは私が来ることができた最も近いものです。16進数、10進数、2進数、PHPのビット演算子などに関する私の知識はほとんどありません。この関数は、1つのエリアまで有効なv4 UUIDを生成します。v4 UUIDの形式は次のとおりです。

xxxxxxxx-xxxx- 4 xxx- y xxx-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;
}

?>

私を助けることができる人に感謝します。


5
あなたは、Linux上で、あなたは少し面倒であれば、あなたはそれらをgenereteことができれば$newId = exec('uuidgen -r');
JorgeGarza

回答:


282

PHPマニュアルのこのコメントから、次のように使用できます。

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
この関数重複作成するため、一意の値が必要な場合は避けてください。mt_rand()は、同じシードが与えられると常に同じ乱数列を生成することに注意してください。したがって、シードが繰り返されるたびに、まったく同じUUIDが生成されます。これを回避するには、時間とMACアドレスを使用してシードする必要がありますが、mt_srand()には整数が必要なため、これをどのように行うかわかりません。
Pavle Predic 2013年

12
@PavlePredic mt_srand(crc32(serialize([microtime(true)、 'USER_IP'、 'ETC']))); (私は別の
ウィリアム

13
PHPのドキュメントでは、mt_rand()は暗号で保護された値を生成しないことを明示的に警告しています。つまり、この関数によって生成される値は予測可能です。UUIDが予測可能でないことを確認する必要がある場合は、openssl_random_pseudo_bytes()関数を使用する以下のジャックのソリューションを使用する必要があります。
リチャードケラー

7
すべてのフィールドをゴミで埋める場合、いったいUUIDを生成する意味は何ですか?
Eevee、2016年

1
PHP 7.0+は関数random_bytes()を定義しています。これは常に暗号的に安全なランダムバイトを生成するか、それができない場合は例外をスローします。これは、openssl_random_psuedo_bytes()でさえ、状況によっては出力が暗号で保護されない場合があるよりも優れています。
thomasrutter

365

個々のフィールドに分割する代わりに、ランダムなデータブロックを生成して個々のバイト位置を変更する方が簡単です。また、mt_rand()よりも優れた乱数ジェネレータを使用する必要があります。

RFC 4122-セクション4.4に従って、これらのフィールドを変更する必要があります:

  1. time_hi_and_version (7番目のオクテットのビット4-7)、
  2. clock_seq_hi_and_reserved (9番目のオクテットのビット6および7)

他の122ビットはすべて十分にランダムである必要があります。

次のアプローチopenssl_random_pseudo_bytes()では、を使用して128ビットのランダムデータを生成し、オクテットに順列を作成してbin2hex()から、vsprintf()最終的な書式設定を行うこと。

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では、次を使用すると、ランダムなバイトシーケンスの生成がさらに簡単になりrandom_bytes()ます。

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

9
openssl拡張機能を持たない* nixユーザーの代替策:$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
Iiridayn

5
また、私はOpenSSLをmt_randよりもはるかに信頼しています。
Falken教授、2013年

3
@BrunoAugustoこれはランダムであり、重複を取得することは非常にまれです(ランダムソースが適切な場合)。ただし、データベースレベルで適用することをお勧めします。
ジャック・

9
random_bytes(16)呼び出しをguidv4関数内に配置せず、パラメーターをguidv4に渡す必要がない理由はありますか?
スティーブンR

7
小さな改善:$ dataにデフォルトのNULLを設定すると、関数の最初の行は次のようになり$data = $data ?? random_bytes( 16 ); ます。独自のランダムデータソースを指定するか、関数に任せることができます。:-)
スティーブンR

118

composerの依存関係を使用している場合は、このライブラリを検討することをお勧めします。https//github.com/ramsey/uuid

これより簡単になることはありません。

Uuid::uuid4();

32
ああ、わからない.... 5行のコードと依存関係のあるライブラリのロード 私はジャックの機能を好む。YMMV
スティーブンR

7
スティーブンへの+1。Ramsey uuidには、単なるuuid4よりも多くの機能があります。私はバナナじゃない!、ここにジャングルがいっぱい!
lcjury

26
UUIDは単なるランダムな文字列ではありません。それがどのように機能するかについての仕様があります。後で拒否されることを心配する必要がない適切なランダムUUIDを生成するには、独自の実装をロールするのではなく、テスト済みのライブラリを使用します。
ブランドン

3
これはUUIDv4です。それは(ほとんどが数ビット)ランダムです。これは暗号化ではありません。「自分自身のローリング」に対するパラノイアはばかげています。
ゴードン

23

UNIXシステムでは、システムカーネルを使用してuuidを生成します。

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

https://serverfault.com/a/529319/210994のクレジットSamveen

注!:このメソッドを使用してuuidを取得すると、実際には非常に迅速にエントロピープールが枯渇します。頻繁に呼び出されるところでは使用しません。


2
移植性に加えて、ランダムソースは/dev/random、エントロピープールが使い果たされた場合にブロックすることに注意してください。
ジャック・

@ジャックUNIXシステムでのエントロピープールの枯渇についてのドキュメントをリンクしてください。このメソッドが機能しない実際的なユースケースについて詳しく知りたいと思います。
ThorSummoner 2014

私はこの特別なカーネルファイルソースをから作成するための情報を見つけることができませんでし/dev/urandomた。私の理解では尽きませんが、重複するuuidを返すリスクがあります。私はそのトレードオフを推測します。実際にシステムエントロピーの影響を受ける一意のIDが本当に必要ですか?
ThorSummoner 2015年

13

v4 uuidを作成するための検索で、私は最初にこのページにアクセスし、次に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));
}

クレジット:pavel.volyntsev

編集:明確にするために、この関数は常にv4 uuid(PHP> = 5.3.0)を提供します。

com_create_guid関数が使用可能な場合(通常はWindowsのみ)、それを使用して中括弧を取り除きます。

存在しない場合(Linux)、この強力なランダムなopenssl_random_pseudo_bytes関数にフォールバックし、vsprintfを使用してv4 uuidにフォーマットします。


5

私の答えはコメントuniqidユーザーコメントに基づいていますが、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));

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

使用するCakePHP場合はCakeText::uuid();CakeTextクラスのメソッドを使用してRFC4122 uuidを生成できます。


5

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

ここでのブローファの答えに触発されまし

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

または、匿名関数を使用できない場合。

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
他の回答のコメントを見ると、mt_rand()ランダム性が保証されていないという人がいます。
Daniel Cheung

3

まったく同じものを検索し、これを自分でほぼ実装しているので、WordPressフレームワーク内でこれを実行している場合、WPには独自の非常に便利な機能があることに言及する価値があると思いました。

$myUUID = wp_generate_uuid4();

ここで説明とソースを読むことができます


1
WP関数はmt_randのみを使用します。十分なランダム性がないかもしれません
ハーバートピーターズ

@HerbertPetersそうですね。それはワンライナーなので、私はそれだけを述べました。あなたがより安全な/保証された乱数を返すことができるように彼らがそれのためにフィルターを追加していたらそれはきちんとなると私は言っていました。しかし、そのfalse反対に、傾向がある場合はreturnを返すこともできます
indextwo

2

mysqlを使用してuuidを生成してみませんか?

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

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

2
MySQLのUUID()関数はv1 uuidを作成します。
staticsan


1

トムから、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])

3
UnixまたはLinux / GNUを実行していない場合はどうなりますか?このコードは機能しません。
Cole Johnson

4
また、/ dev / randomが空で、より多くのエントロピーがリロードされるのを待っている場合、実行速度が非常に遅くなる可能性もあります。
ObsidianX

1
/dev/urandom大丈夫です- /dev/random長期的な暗号化キーの生成にのみ使用してください。
Iiridayn 2013

これに基づいて、私はこれを思いつきました-ランダム性のいくつかの可能なソースをフォールバックとして使用し、mt_rand()より優れたものがない場合はシードに頼ります。
mindplay.dk

1
今ではrandom_bytes()、PHP 7で使用するだけですぐに使えます:-)
mindplay.dk 2017年

1

私は10進数のバイナリからの変換を行うために、よりエレガントな方法があります確信している4xxxyxxx部分ます。しかしopenssl_random_pseudo_bytes、暗号化された安全な数値ジェネレータとして使用したい場合は、これを使用します。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.