PHP function to generate v4 UUID

277,419

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:

  1. time_hi_and_version (bits 4-7 of 7th octet),
  2. 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));
Share:
277,419

Related videos on Youtube

anomareh
Author by

anomareh

Updated on February 02, 2022

Comments

  • anomareh
    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
      JorgeGarza over 7 years
      If you are on Linux and if you are a little lazy you can generete them with $newId = exec('uuidgen -r');
    • abmmhasan
      abmmhasan over 2 years
      You may consider using this library: github.com/abmmhasan/UUID then simply use the command: \AbmmHasan\Uuid::v4();
  • anomareh
    anomareh over 14 years
    Yeah, 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
    Cole Tobin over 11 years
    What if they aren't running Unix or Linux/GNU? This code won't work.
  • ObsidianX
    ObsidianX over 11 years
    This also has the potential of running very slowly if /dev/random is empty and is waiting for more entropy to reload.
  • Pavle Predic
    Pavle Predic about 11 years
    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.
  • Wiliam
    Wiliam about 11 years
    @PavlePredic mt_srand(crc32(serialize([microtime(true), 'USER_IP', 'ETC']))); (i'm another wiliam :P)
  • Iiridayn
    Iiridayn about 11 years
    /dev/urandom should be fine - /dev/random should only be used for generation of long term cryptographic keys.
  • Iiridayn
    Iiridayn about 11 years
    An alternative for *nix users who don't have the openssl extension: $data = file_get_contents('/dev/urandom', NULL, NULL, 0, 16);
  • Prof. Falken
    Prof. Falken almost 11 years
    Also, I would trust OpenSSL a lot more than mt_rand.
  • Richard Keller
    Richard Keller almost 11 years
    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.
  • Ja͢ck
    Ja͢ck about 10 years
    Besides portability, note that the random source is /dev/random which blocks if the entropy pool is exhausted.
  • ThorSummoner
    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
    mindplay.dk almost 9 years
    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.
  • ThorSummoner
    ThorSummoner almost 9 years
    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?
  • Eevee
    Eevee almost 8 years
    what on earth is the point of generating a UUID if you fill every field with garbage?
  • Bruno Augusto
    Bruno Augusto almost 8 years
    Many 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
    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
    Daniel Cheung almost 8 years
    If you look at the comments in other answers, you would see people saying mt_rand() is not guaranteed randomness.
  • Stephen R
    Stephen R about 7 years
    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?
  • mindplay.dk
    mindplay.dk almost 7 years
    By now, just use random_bytes() in PHP 7 and off you go :-)
  • Deva
    Deva over 6 years
    MySQL's UUID() function creates v1 uuids.
  • Ja͢ck
    Ja͢ck over 6 years
    @StephenR it allows for the caller to choose how they want to generate the random data.
  • Stephen R
    Stephen R almost 6 years
    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. :-)
  • Stephen R
    Stephen R almost 6 years
    Oh, I don't know.... Five lines of code vs. loading a library with dependencies? I prefer Jack's function. YMMV
  • lcjury
    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
    Brandon over 5 years
    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.
  • Gordon
    Gordon over 5 years
    It'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
    Herbert Peters about 5 years
    The WP Function uses mt_rand exclusively. So might not have enough randomness
  • indextwo
    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
    thomasrutter almost 5 years
    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.
  • KFoobar
    KFoobar over 4 years
    Please add an explanation to your code to help others understand what it does.
  • Serhii Polishchuk
    Serhii Polishchuk almost 4 years
    this is what actually done by Symfony polyfil - github.com/symfony/polyfill-uuid/blob/master/Uuid.php#L320
  • istepaniuk
    istepaniuk over 3 years
    The overhead of using the library is non-existent, and it has tests. +1 for not reinventing the wheel.
  • ThorSummoner
    ThorSummoner over 3 years
    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
  • jack
    jack almost 3 years
    These kind of solutions are really funny to me. Funny != bad
  • ThorSummoner
    ThorSummoner almost 3 years
    Systemd 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
    undefined almost 3 years
    @Iiridayn or uuidgen from util-linux 2.34
  • atmacola
    atmacola almost 3 years
    uuid_create(UUID_TYPE_TIME) to include the date. Note: this give a true UUID, not a fake one
  • Admin
    Admin over 2 years
    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.
  • Ryan Rentfro
    Ryan Rentfro over 2 years
    When 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
    Rafael over 2 years
    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.