Reputation: 5574
So I've been doing some digging around and I've been trying to piece together a function that generates a valid v4 UUID in PHP. This is the closest I've been able to come. My knowledge in hex, decimal, binary, PHP's bitwise operators and the like is nearly nonexistent. This function generates a valid v4 UUID up until one area. A v4 UUID should be in the form of:
xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
Where y is 8, 9, A, or B. This is where the functions fails as it doesn't adhere to that.
I was hoping someone with more knowledge than me in this area could lend me a hand and help me fix this function so it does adhere to that rule.
The function is as follows:
<?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;
}
?>
Upvotes: 333
Views: 378014
Reputation: 173582
To construct a UUIDv4 you can generate 128 bits worth of random data, patch a few fields to make the data comply with the standard, and then format it as hexadecimal groups.
According to RFC 4122 - Section 4.4, you need to change these bits:
time_hi_and_version
(bits 4-7 of the 7th octet),clock_seq_hi_and_reserved
(bit 6 & 7 of the 9th octet)The below code makes the permutations on the given data and then uses bin2hex()
and vsprintf()
to do the final formatting.
function uuidv4()
{
$data = 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));
}
The random_bytes
function was introduced in PHP 7.0. An example of how this can be implemented in older versions of PHP:
$data = openssl_random_pseudo_bytes(16, $strong);
// ensure the result is cryptographically strong
assert($data !== false && $strong);
To generate cryptographically strong random data, instead of using mt_rand()
, it's recommended to use either openssl_random_pseudo_bytes()
or random_bytes()
(php7 onwards). You can also choose another method for generating the random data.
Upvotes: 483
Reputation: 981
The uuid_create function, which is available in PHP 7.2 and later, can be used.
$uuid = uuid_create();
echo $uuid;
Upvotes: -1
Reputation: 101
Cryptographically secure UUID v4 for PHP >= 7.
<?php
function uuid4() {
/* 32 random HEX + space for 4 hyphens */
$out = bin2hex(random_bytes(18));
$out[8] = "-";
$out[13] = "-";
$out[18] = "-";
$out[23] = "-";
/* UUID v4 */
$out[14] = "4";
/* variant 1 - 10xx */
$out[19] = ["8", "9", "a", "b"][random_int(0, 3)];
return $out;
}
echo uuid4();
output: c68469d2-065b-4b17-b36f-5c40efb5f6cd
Upvotes: 7
Reputation: 917
Just an idea, but what I ended up doing to get a V4 GUID was to use the database server. I am using SQL Server, and in the scenario where I needed the GUID, I was already running a query, so I just added newid() as one of the query result fields. That gave me the V4 GUID I needed.
This obviously depends on the database server you are using, and what else is going on in the code where you need the GUID (and how many GUIDs you need), but if your DB server generates v4 GUID, and especially if you are running a query anyway, that is a fast and simple PHP-version-independent way to get your GUID.
Upvotes: -1
Reputation: 21
This could be simpler?
$uuid = bin2hex(openssl_random_pseudo_bytes(16));
for($cnt = 8; $cnt <=23; $cnt+=5)
$uuid = substr($uuid, 0, $cnt) . "-" . substr($uuid, $cnt);
echo $uuid . "\n";
Upvotes: 2
Reputation: 337
// php version >= 7
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));
Upvotes: 32
Reputation: 1508
Use Symfony Polyfill / Uuid
Then you can just generate uuid as native php function:
$uuid = uuid_create(UUID_TYPE_RANDOM);
More about it, read in official Symfony blop post - https://symfony.com/blog/introducing-the-new-symfony-uuid-polyfill
Upvotes: 8
Reputation: 20776
A slight variation on Jack's answer to add support for 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));
}
Upvotes: 28
Reputation: 630
I'm sure there's a more elegant way to do the conversion from binary to decimal for the 4xxx
and yxxx
portions. But if you want to use openssl_random_pseudo_bytes
as your crytographically secure number generator, this is what I use:
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))
);
Upvotes: 2
Reputation: 5935
Having searched for the exact same thing and almost implementing a version of this myself, I thought it was worth mentioning that, if you're doing this within a WordPress framework, WP has its own super-handy function for exactly this:
$myUUID = wp_generate_uuid4();
You can read the description and the source here.
Upvotes: 6
Reputation: 680
In my search for a creating a v4 uuid, I came first to this page, then found this on 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));
}
credit: pavel.volyntsev
Edit: to clarify, this function will always give you a v4 uuid (PHP >= 5.3.0).
When the com_create_guid function is available (usually only on Windows), it will use that and strip the curly braces.
If not present (Linux), it will fall back on this strong random openssl_random_pseudo_bytes function, it will then uses vsprintf to format it into v4 uuid.
Upvotes: 20
Reputation: 3419
If you use CakePHP
you can use their method CakeText::uuid();
from the CakeText class to generate a RFC4122 uuid.
Upvotes: 7
Reputation: 18109
on unix systems, use the system kernel to generate a uuid for you.
file_get_contents('/proc/sys/kernel/random/uuid')
Credit Samveen on https://serverfault.com/a/529319/210994
Note!: Using this method to get a uuid does in fact exhaust the entropy pool, very quickly! I would avoid using this where it would be called frequently.
Upvotes: 43
Reputation: 376
How about using mysql to generate the uuid for you?
$conn = new mysqli($servername, $username, $password, $dbname, $port);
$query = 'SELECT UUID()';
echo $conn->query($query)->fetch_row()[0];
Upvotes: 3
Reputation: 2821
Anyone using composer dependencies, you might want to consider this library: https://github.com/ramsey/uuid
It doesn't get any easier than this:
Uuid::uuid4();
Upvotes: 160
Reputation: 12806
Inspired by broofa's answer here.
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');
Or if unable to use anonymous functions.
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');
Upvotes: 2
Reputation: 3780
My answer is based on comment uniqid user comment but it uses openssl_random_pseudo_bytes function to generate random string instead of reading from /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
Upvotes: 6
Reputation: 19
From tom, on 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])
Upvotes: 1
Reputation: 15593
Taken from this comment on the PHP manual, you could use this:
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 )
);
}
Upvotes: 334