337

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

?>
3
  • 12
    If you are on Linux and if you are a little lazy you can generete them with $newId = exec('uuidgen -r'); Commented Jan 5, 2017 at 19:46
  • 1
    You may consider using this library: github.com/abmmhasan/UUID then simply use the command: \AbmmHasan\Uuid::v4(); Commented Nov 19, 2021 at 16:08
  • By the way, instead of a randomly generated Version 4 UUID, consider using the new Version 7 UUID defined in RFC 9562, based on a timestamp plus random bits, arranged in a way to enable efficient indexing. Commented Oct 12 at 17:49

19 Answers 19

488

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:

  1. time_hi_and_version (bits 4-7 of the 7th octet),
  2. 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));
}

PHP 5 and older

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

Generating random data

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.

Sign up to request clarification or add additional context in comments.

9 Comments

An alternative for *nix users who don't have the openssl extension: $data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
Also, I would trust OpenSSL a lot more than mt_rand.
@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.
Is 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?
Small 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. :-)
|
336

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

6 Comments

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.
|
159

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

9 Comments

Oh, I don't know.... Five lines of code vs. loading a library with dependencies? I prefer Jack's function. YMMV
+1 to Stephen. Ramsey uuid has a lot more functionality than just uuid4. I wan't a banana!, here you have the entire jungle!
UUID'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.
It's a UUIDv4. It's (mostly, but for a few bits) random. This ain't cryptography. Paranoia against "rolling your own" is silly.
The overhead of using the library is non-existent, and it has tests. +1 for not reinventing the wheel.
|
46

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.

6 Comments

Besides portability, note that the random source is /dev/random which blocks if the entropy pool is exhausted.
@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.
I 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?
I 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
use trim(file_get_contents('/proc/sys/kernel/random/uuid')) Returns a new line on the end by default, which you probably don't want. Entropy not a problem since kernel 5.6, released 29 Mar 2020
|
33
// php version >= 7
$uuid = vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex(random_bytes(16)), 4));

3 Comments

Please add an explanation to your code to help others understand what it does.
this is what actually done by Symfony polyfil - github.com/symfony/polyfill-uuid/blob/master/Uuid.php#L320
Is 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.
28

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

Comments

21

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.

Comments

9

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

1 Comment

uuid_create(UUID_TYPE_TIME) to include the date. Note: this give a true UUID, not a fake one
8

If you use CakePHP you can use their method CakeText::uuid(); from the CakeText class to generate a RFC4122 uuid.

Comments

7

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

Comments

6

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

Comments

6

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.

2 Comments

The WP Function uses mt_rand exclusively. So might not have enough randomness
@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 🤷
2

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

1 Comment

If you look at the comments in other answers, you would see people saying mt_rand() is not guaranteed randomness.
2

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

1 Comment

MySQL's UUID() function creates v1 uuids.
2

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

1 Comment

i think this is the most elegant solution (function names are long and ugly, but they are what they are)
2

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

1 Comment

As 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.
1

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

5 Comments

What if they aren't running Unix or Linux/GNU? This code won't work.
This also has the potential of running very slowly if /dev/random is empty and is waiting for more entropy to reload.
/dev/urandom should be fine - /dev/random should only be used for generation of long term cryptographic keys.
Based 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.
By now, just use random_bytes() in PHP 7 and off you go :-)
-1

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.

Comments

-1

The uuid_create function, which is available in PHP 7.2 and later, can be used.

      $uuid = uuid_create();
      echo $uuid;

1 Comment

On PHP 8.1, this doesn't work for me. Perhaps you're using other libraries.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.