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 non existant. 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;
}
?>
Thanks to anyone that can help me out.
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 )
);
}
This function will create duplicates, so avoid it when you need unique values. Note that mt_rand() will always produce the same sequence of random numbers given the same seed. So every time a seed is repeated, the same exact UUID is generated. To get around this, you would need to seed it using time and mac address, but I'm not sure how you would do this, since mt_srand() requires an integer.
@PavlePredic mt_srand(crc32(serialize([microtime(true), 'USER_IP', 'ETC']))); (i'm another wiliam :P)
The PHP docs explicitly caution that mt_rand() does not generate cryptographically secure values. In other words, values generated by this function may be predictable. If you need to ensure that the UUIDs are not predictable, you should rather use Jack's solution below, which makes use of the openssl_random_pseudo_bytes() function.
what on earth is the point of generating a UUID if you fill every field with garbage?
PHP 7.0+ defines the function random_bytes() which will always generate cryptographically secure random bytes or throw an exception if it's unable to. This is better than even openssl_random_psuedo_bytes() whose output is sometimes not cryptographically secure under some circumstances.