UUID(Universally Unique Identifier)は、システム上でデータを重複なく一意に識別するための128ビットの識別子です。中でもUUIDv4は乱数をもとに生成されるバージョンで、PHPでは外部ライブラリを使わずに標準関数だけでも作成できます。
この記事では、PHPでUUID(UUIDv4)を生成する方法を、標準関数による自作からライブラリの利用まで複数パターン紹介します。まずは追加ライブラリなしで動くサンプルコードから見ていきましょう。
PHPでUUIDを作る最短コード
「uuid php」「php uuid」どちらのキーワードでたどり着いた方も、まずは最短で動くコードから確認できます。追加ライブラリが不要なら、標準関数の random_bytes だけで数行のUUIDv4生成が可能です。
<?php
// 追加ライブラリ不要・PHP 7.0以降で動く最短のUUIDv4生成
$data = random_bytes(16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // バージョン(4)を設定
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // バリアントを設定
echo vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
// 例: 3f2504e0-4f89-41d3-9a0c-0305e82c3301Composerが使える環境なら、ramsey/uuid を導入して Uuid::uuid4()->toString() の1行でも生成できます。以降で、標準関数での自作・Composerライブラリ・Laravelそれぞれの実装と違いを詳しく解説します。
random_bytes を使った自作サンプルコード
<?php
function generateUUIDv4() {
$data = random_bytes(16);
// バージョンを設定 (4はUUIDv4を表す)
$data[6] = chr(ord($data[6]) & 0x0f | 0x40);
// RFC 4122 バリアントを設定
$data[8] = chr(ord($data[8]) & 0x3f | 0x80);
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
var_dump(generateUUIDv4()); // string(36) "2fe46dc9-cdce-4c94-a2c8-2096bd317b51"
var_dump(generateUUIDv4()); // string(36) "e99e26d4-ef7b-4fe7-93a4-b463e5ad6dff"
var_dump(generateUUIDv4()); // string(36) "1fa22622-5270-4adf-ad57-7faf4ef607ff"
PHPでUUIDv4を生成するためには、上記のサンプルコードで作成できます。
このコードは、PHPのrandom_bytes関数を使用してランダムなバイト列を生成し、それをUUIDv4の仕様に従って整形しています。生成されたUUIDv4は文字列として返されます。
UUIDv4は仕様上ではランダムに生成されるものになりますが、UUIDに共通するバージョン情報の指定とバリアントの指定をすることが必要になるのでその分の指定だけを行なっています。
UUIDとは
UUID(Universally Unique Identifier)とは、ソフトウェア上でオブジェクトを一意に識別するための識別子である。UUIDは128ビットの数値だが、16進法による
https://ja.wikipedia.org/wiki/UUID550e8400-e29b-41d4-a716-446655440000というような文字列による表現が使われることが多い。
つまり、「(8文字)-(4文字)-(4文字)-(4文字)-(12文字)」形式の16進法の32文字の文字列のことを言います。
ちなみに、大文字と小文字は同一と判定されます。
UUIDv4とは
バージョン4のUUIDは、乱数により生成される。他のUUIDと同様、バージョン4であることを示すために4ビットが使われ、バリアント(バリアント1と2に対して、それぞれ102または1102)を示すために2または3ビットが使われる。
https://ja.wikipedia.org/wiki/UUID#バージョン4
~~~略~~~
16進表記では、RRRRRRRR-RRRR-4RRR-rRRR-RRRRRRRRRRRRとなる。
バリアントとは
UUIDには歴史的経緯から数種類のバリアント(変種)があり、現行の規格で定められているのはそのうちの1つである。16進表記をした場合に
https://ja.wikipedia.org/wiki/UUID#バリアントxxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxxのNの桁の上位ビットがバリアントを示す。現行の規格では上位2ビットが102であることが定められているので、16進表記では8、9、A、Bのいずれかとなる。
現行の規格、と表現されている部分には「RFC4122」が採用されているのが一般的な様子です。
つまりはUUIDv4である事は
xxxxxxxx-xxxx-4xxx-{8,9,a,b}xxx-xxxxxxxxxxxx
で表記されている物が相当します。
冒頭で紹介したサンプルコードを実行しまくった結果が以下です
$ php sample.php
string(36) "2fe46dc9-cdce-4c94-a2c8-2096bd317b51"
string(36) "e99e26d4-ef7b-4fe7-93a4-b463e5ad6dff"
string(36) "1fa22622-5270-4adf-ad57-7faf4ef607ff"
$ php sample.php
string(36) "0a47e8d0-fe99-4e24-ac74-02d9f4aaa396"
string(36) "7f9df017-182f-4412-9d29-e833936ed553"
string(36) "d1ba8635-2fc5-4a9a-a99d-6cf301baa486"
$ php sample.php
string(36) "a1c50510-e9f9-40c4-b117-f9819d79b72e"
string(36) "547e3e45-a86c-4ee9-b972-61d0d6eb9861"
string(36) "dd5448d5-9521-40e5-afbc-6dab8d595c02"
$ php sample.php
string(36) "5aeb1850-cd6d-47be-a641-92d224d160a6"
string(36) "5e6cb333-6b01-4406-8690-c4f77bd17bca"
string(36) "361495f5-557e-4809-8ae2-d26b4b4be00d"
$ php sample.php
string(36) "e44ac1f5-7d70-4720-b5cc-b9a81868609b"
string(36) "8ae16f4a-e3ea-4aee-9d7c-4ea7afb704f9"
string(36) "f5e36476-a66d-45a6-9e3a-edb2e109f22c"
$ php sample.php
string(36) "4f3ad84b-3a33-457a-bd13-08f4358a1934"
string(36) "c68d8326-4f60-438d-a92b-e40549e2c88b"
string(36) "029ead16-10e6-4a43-8879-c9b5ce859169"ハイフン(-)で区切った3つ目のブロックでは常に「4」が表示され、4つ目のブロックは「8、9、a、b」しか表示されていないことがわかると思います。
ライブラリを使ってUUIDを生成する方法
自作コードでも生成できますが、実務ではメンテナンス性や信頼性の観点からライブラリを使うのが一般的です。ここでは代表的な2つのライブラリでUUIDv4を生成する方法を紹介します。
ramsey/uuid で生成する
PHPで最も広く使われているUUIDライブラリが ramsey/uuid です。後述するLaravelの Str::uuid() も内部でこのライブラリを利用しています。
composer require ramsey/uuid<?php
use Ramsey\Uuid\Uuid;
$uuid = Uuid::uuid4();
echo $uuid->toString(); // 例: 9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6dUuid::uuid4() がUUIDv4を生成するメソッドで、toString() で文字列表現を取得できます。バージョンやバリアントの設定を意識せずに、RFC 4122準拠のUUIDv4が得られます。
symfony/uid で生成する
Symfonyコンポーネントの symfony/uid でもUUIDv4を生成できます。Symfony以外のプロジェクトでも単体で利用可能です。
composer require symfony/uid<?php
use Symfony\Component\Uid\Uuid;
$uuid = Uuid::v4();
echo $uuid->toRfc4122(); // 例: d9e7a184-5d5b-4f1e-9a3f-4d4b6c8f0a12Uuid::v4() でUUIDv4を生成し、toRfc4122() で一般的なハイフン区切りの文字列を取得します。
どの方法を選ぶかの目安としては、依存を増やしたくない・仕組みを理解したい場合は冒頭の random_bytes による自作、実務で安定した生成やUUIDのパースなども扱いたい場合は ramsey/uuid または symfony/uid のライブラリ利用がおすすめです。
LaravelのUUIDv4実装コードと比較
PHPでUUIDv4を生成するコードとしては身近なものとしてはLaravelの Str::uuid() が存在します。こちらの実装方法についても確認してみます。
/**
* Generate a UUID (version 4).
*
* @return \Ramsey\Uuid\UuidInterface
*/
public static function uuid()
{
return static::$uuidFactory
? call_user_func(static::$uuidFactory)
: Uuid::uuid4();
} /**
* Version 4 (random) UUID
*
* @link https://tools.ietf.org/html/rfc4122#section-4.1.3 RFC 4122, § 4.1.3: Version
*/
public const UUID_TYPE_RANDOM = 4;
/**
* Returns a version 4 (random) UUID
*
* @return UuidInterface A UuidInterface instance that represents a
* version 4 UUID
*/
public static function uuid4(): UuidInterface
{
return self::getFactory()->uuid4();
} public function uuid4(): UuidInterface
{
$bytes = $this->randomGenerator->generate(16);
return $this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_RANDOM);
}
/**
* Returns an RFC 4122 variant Uuid, created from the provided bytes and version
*
* @param string $bytes The byte string to convert to a UUID
* @param int $version The RFC 4122 version to apply to the UUID
*
* @return UuidInterface An instance of UuidInterface, created from the
* byte string and version
*
* @psalm-pure
*/
private function uuidFromBytesAndVersion(string $bytes, int $version): UuidInterface
{
/** @var array $unpackedTime */
$unpackedTime = unpack('n*', substr($bytes, 6, 2));
$timeHi = (int) $unpackedTime[1];
$timeHiAndVersion = pack('n*', BinaryUtils::applyVersion($timeHi, $version));
/** @var array $unpackedClockSeq */
$unpackedClockSeq = unpack('n*', substr($bytes, 8, 2));
$clockSeqHi = (int) $unpackedClockSeq[1];
$clockSeqHiAndReserved = pack('n*', BinaryUtils::applyVariant($clockSeqHi));
$bytes = substr_replace($bytes, $timeHiAndVersion, 6, 2);
$bytes = substr_replace($bytes, $clockSeqHiAndReserved, 8, 2);
if ($this->isDefaultFeatureSet) {
return LazyUuidFromString::fromBytes($bytes);
}
/** @psalm-suppress ImpureVariable */
return $this->uuid($bytes);
}と流れを追っていくと
$this->randomGenerator->generate(16) で16バイトのランダムなバイト列を生成し、$this->uuidFromBytesAndVersion($bytes, Uuid::UUID_TYPE_RANDOM) を呼び出している事がわかります。
uuidFromBytesAndVersion関数のコメントでは「Returns an RFC 4122 variant Uuid, created from the provided bytes and version」とあり、RFC4122のバリアントを指定していることがわかります。
処理の中では
$bytes = substr_replace($bytes, $timeHiAndVersion, 6, 2);
$bytes = substr_replace($bytes, $clockSeqHiAndReserved, 8, 2);
と作成したバイト列の6と8の置換処理を行っており、各種変数ではバージョン指定とバリアントの値が入っているため、冒頭で紹介したサンプルコードと同様に置換を行なっています。
実際に実行した場合はこちら
$ php artisan --version
Laravel Framework 10.20.0
$ php artisan tinker
Psy Shell v0.11.20 (PHP 8.1.12 — cli) by Justin Hileman
> Str::uuid();
= Ramsey\Uuid\Lazy\LazyUuidFromString {#8082
uuid: "43e2cec4-cc87-4bc2-8b4a-20efa64514e9",
}
> Str::uuid();
= Ramsey\Uuid\Lazy\LazyUuidFromString {#8105
uuid: "2f5686c6-cf9b-495c-931a-e5eaa2c1cc6c",
}
> UUIDv4の値が出力されます。
つまり、Laravelに実装されているUUIDの出力内容と大差無い実装であると言えるかと感じています。
自作・Composerライブラリ・Laravelの違いまとめ
PHPでUUID(UUIDv4)を生成する各方法の違いを整理すると、次のとおりです。
| 方法 | インストール | 生成コード | 向いているケース |
|---|---|---|---|
| 標準関数で自作(random_bytes) | 不要 | random_bytes + vsprintf | 依存を増やしたくない・仕組みを理解したい |
| ramsey/uuid(Composer) | composer require ramsey/uuid | Uuid::uuid4()->toString() | 実務で安定した生成やパースも扱いたい |
| symfony/uid(Composer) | composer require symfony/uid | Uuid::v4()->toRfc4122() | Symfony系プロジェクトや軽量に使いたい |
| Laravel(Str::uuid) | Laravel標準(内部でramsey/uuid) | Str::uuid() | Laravelアプリ内で手軽に生成したい |
Laravelの Str::uuid() は内部で ramsey/uuid を利用しているため、Composerで ramsey/uuid を導入した場合と生成結果は実質同じです。フレームワークを使っていない環境でも、標準関数による自作かComposerライブラリの導入でUUIDv4を生成できます。
終わりに
以上、PHPでUUIDv4を作成するサンプルコードについて紹介しました。
LaravelなどのフレームワークやPHPライブラリを導入すれば早く解決するかとは思いますが、UUIDの仕様はそこまで複雑では無いので独自実装として組み込むことも可能かと感じています。
将来的な保守を考えるとフレームワークに実装されている物を使用した方がいいかとは思いますが、実装の方法や仕様を紐解いていくのは楽しい作業であり、自分自身のいい経験になりました。
何か間違いがありましたらコメントにて報告いただけますと大変助かります。
UUIDと類似しているユニークIDを生成するためのuniqid()についての記事も書きました。こちらご興味あればご覧になってください。
今回もお疲れ様でした。

コメントを残す