PHP function to generate v4 UUID
Solution 1
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 )
);
}
Solution 2
Instead of breaking it down into individual fields, it's easier to generate a random block of data and change the individual byte positions. You should also use a better random number generator than mt_rand().
According to RFC 4122 - Section 4.4, you need to change these fields:
-
time_hi_and_version
(bits 4-7 of 7th octet), -
clock_seq_hi_and_reserved
(bit 6 & 7 of 9th octet)
All of the other 122 bits should be sufficiently random.
The following approach generates 128 bits of random data using openssl_random_pseudo_bytes()
, makes the permutations on the octets and then uses bin2hex()
and vsprintf()
to do the final formatting.
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));
With PHP 7, generating random byte sequences is even simpler using random_bytes()
:
function guidv4($data = null)
{
$data = $data ?? random_bytes(16);
// ...
}
Solution 3
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();
Solution 4
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.
Solution 5
// php version >= 7
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));
Related videos on Youtube
anomareh
Updated on February 02, 2022Comments
-
anomareh over 2 years
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; } ?>
-
JorgeGarza over 7 yearsIf you are on Linux and if you are a little lazy you can generete them with
$newId = exec('uuidgen -r');
-
abmmhasan over 2 yearsYou may consider using this library: github.com/abmmhasan/UUID then simply use the command: \AbmmHasan\Uuid::v4();
-
-
anomareh over 14 yearsYeah, wow. Coulda' sworn I checked through the comments on uniqid too. Just a few questions about that function compared to mine though. Is there any difference in how the chunks are generated? I.E. for the node generating it in 3 16 bit chunks vs 6 8 bit chunks? Lastly, any differences vs the bit shifting and just grabbing from mt_rand()? Thanks again.
-
Cole Tobin over 11 yearsWhat if they aren't running Unix or Linux/GNU? This code won't work.
-
ObsidianX over 11 yearsThis also has the potential of running very slowly if /dev/random is empty and is waiting for more entropy to reload.
-
Pavle Predic about 11 yearsThis 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.
-
Wiliam about 11 years@PavlePredic mt_srand(crc32(serialize([microtime(true), 'USER_IP', 'ETC']))); (i'm another wiliam :P)
-
Iiridayn about 11 years
/dev/urandom
should be fine -/dev/random
should only be used for generation of long term cryptographic keys. -
Iiridayn about 11 yearsAn alternative for *nix users who don't have the openssl extension:
$data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
-
Prof. Falken almost 11 yearsAlso, I would trust OpenSSL a lot more than mt_rand.
-
Richard Keller almost 11 yearsThe 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.
-
Ja͢ck about 10 yearsBesides portability, note that the random source is
/dev/random
which blocks if the entropy pool is exhausted. -
ThorSummoner about 10 years@Jack Would you kindly link some documentation on the topic of entropy pool exhaustion on unix systems please? I'd be interested to know more about a realistic use case where this method breaks down.
-
mindplay.dk almost 9 yearsBased on that, I came up with this - it uses several possible sources of randomness as fall-backs, and resorts to seeding
mt_rand()
if nothing fancier is available. -
ThorSummoner almost 9 yearsI was unable to find information on making this special kernel file source from
/dev/urandom
, which in my understanding wouldn't exhaust, but risks returning duplicate uuids. I guess its a tradeoff; do you really actually need a unique id influenced by system entropy? -
Eevee almost 8 yearswhat on earth is the point of generating a UUID if you fill every field with garbage?
-
Bruno Augusto almost 8 yearsMany years later I stumbled upon your answer and, although I've searched myself, I have a question: is this random or in a practical scenario should I add an additional verification on database that looks after the sequence generated?
-
Ja͢ck almost 8 years@BrunoAugusto it's random, and it's extremely unlikely (with a good random source) to get duplicates, but it's a good practice to enforce it at database level.
-
Daniel Cheung almost 8 yearsIf you look at the comments in other answers, you would see people saying
mt_rand()
is not guaranteed randomness. -
Stephen R about 7 yearsIs there any reason to NOT put the random_bytes(16) call inside the guidv4 function and thus not have to pass any parameter to guidv4?
-
mindplay.dk almost 7 yearsBy now, just use
random_bytes()
in PHP 7 and off you go :-) -
Deva over 6 yearsMySQL's
UUID()
function creates v1 uuids. -
Ja͢ck over 6 years@StephenR it allows for the caller to choose how they want to generate the random data.
-
Stephen R almost 6 yearsSmall improvement: Set a NULL default for $data, and then the first line of the function is this:
$data = $data ?? random_bytes( 16 );
Now you CAN specify your own random data source, or let the function do it for you. :-) -
Stephen R almost 6 yearsOh, I don't know.... Five lines of code vs. loading a library with dependencies? I prefer Jack's function. YMMV
-
lcjury almost 6 years+1 to Stephen. Ramsey uuid has a lot more functionality than just uuid4. I wan't a banana!, here you have the entire jungle!
-
Brandon over 5 yearsUUID's aren't just random strings. There is a spec to how it works. To generate a proper random UUID that I don't have to worry about getting rejected later, I'd rather use a tested library than roll my own implementation.
-
Gordon over 5 yearsIt's a UUIDv4. It's (mostly, but for a few bits) random. This ain't cryptography. Paranoia against "rolling your own" is silly.
-
Herbert Peters about 5 yearsThe WP Function uses mt_rand exclusively. So might not have enough randomness
-
indextwo about 5 years@HerbertPeters You're right. I only mentioned it because it's a one-liner. I was going to say that it would be neat if they had added a filter for it so you could return a more secure/guaranteed-random number; but the flipside of that is that, if you were so inclined, you could also return
false
🤷 -
thomasrutter almost 5 yearsPHP 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.
-
KFoobar over 4 yearsPlease add an explanation to your code to help others understand what it does.
-
Serhii Polishchuk almost 4 yearsthis is what actually done by Symfony polyfil - github.com/symfony/polyfill-uuid/blob/master/Uuid.php#L320
-
istepaniuk over 3 yearsThe overhead of using the library is non-existent, and it has tests. +1 for not reinventing the wheel.
-
ThorSummoner over 3 yearsI noticed once upon a time, that fetching a uuid via the linux kernel (a shared resource) was sufficient to guarantee unique uuids on the same system. I believe this procfs uuid is safe to use in that way. Be aware that there are multiple versions of UUID en.wikipedia.org/wiki/… and linux in general probably give you Version 3 and 5 types man7.org/linux/man-pages/man3/uuid_generate.3.html
-
jack almost 3 yearsThese kind of solutions are really funny to me. Funny != bad
-
ThorSummoner almost 3 yearsSystemd also provides a "get a uuid" interface somewhere, in testing I found that they all ultimately use the same kernal uuid generator (because we're using linux as the shared resource to ensure uuid locks), so no matter what interface your using (proc fs, syscall, systemd bus) its the same lock and same generating code, so idk, getting the file doesn't hurt me any
-
undefined almost 3 years@Iiridayn or
uuidgen
from util-linux 2.34 -
atmacola almost 3 yearsuuid_create(UUID_TYPE_TIME) to include the date. Note: this give a true UUID, not a fake one
-
Admin over 2 yearsAs it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.
-
Ryan Rentfro over 2 yearsWhen you assemble strings of random bits they are just that. A UUID4 has 6 pre-determined bits of the total 128 that identify the type of the UUID and it's encoding. 5 lines of code is not compliant with the spec and dangerous to run in high concurrenct situations due to how you would assemble random strings with PHP.
-
Rafael over 2 yearsIs this correct? A quick test returned
c0a062b7-b225-c294-b8a0-06b98931a45b
, which doesn't match with xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx. It returned a c instead of 4.