杂项 函数
在线手册:中文 英文
PHP手册

uniqid

(PHP 4, PHP 5)

uniqid生成一个唯一ID

说明

string uniqid ([ string $prefix = "" [, bool $more_entropy = false ]] )

获取一个带前缀、基于当前时间微秒数的唯一ID。

参数

prefix

有用的参数。例如:如果在多台主机上可能在同一微秒生成唯一ID。

prefix为空,则返回的字符串长度为13。more_entropyTRUE,则返回的字符串长度为23。

more_entropy

如果设置为 TRUEuniqid() 会在返回的字符串结尾增加额外的煽(使用combined linear congruential generator)。 使得唯一ID更具唯一性。

返回值

返回字符串形式的唯一ID。

范例

Example #1 uniqid() 例子

<?php
/* A uniqid, like: 4b3403665fea6 */
printf("uniqid(): %s\r\n"uniqid());

/* We can also prefix the uniqid, this the same as 
 * doing:
 *
 * $uniqid = $prefix . uniqid();
 * $uniqid = uniqid($prefix);
 */
printf("uniqid('php_'): %s\r\n"uniqid('php_'));

/* We can also activate the more_entropy parameter, which is 
 * required on some systems, like Cygwin. This makes uniqid()
 * produce a value like: 4b340550242239.64159797
 */
printf("uniqid('', true): %s\r\n"uniqid(''true));
?>

更新日志

版本 说明
5.0.0 prefix 参数设为可选。
4.3.1 prefix 参数的长度限制提升到114个字符。

注释

Note:

在Cygwin环境下,为了使此函数能够工作,more_entropy 必须设置为 TRUE


杂项 函数
在线手册:中文 英文
PHP手册
PHP手册 - N: 生成一个唯一ID

用户评论:

redtraider at gmail dot com (14-Feb-2012 08:37)

I use this function to generate microsoft-compatible GUID's.

<?php
 
public function create_guid($namespace = '') {    
    static
$guid = '';
   
$uid = uniqid("", true);
   
$data = $namespace;
   
$data .= $_SERVER['REQUEST_TIME'];
   
$data .= $_SERVER['HTTP_USER_AGENT'];
   
$data .= $_SERVER['LOCAL_ADDR'];
   
$data .= $_SERVER['LOCAL_PORT'];
   
$data .= $_SERVER['REMOTE_ADDR'];
   
$data .= $_SERVER['REMOTE_PORT'];
   
$hash = strtoupper(hash('ripemd128', $uid . $guid . md5($data)));
   
$guid = '{' .  
           
substr($hash08) .
           
'-' .
           
substr($hash84) .
           
'-' .
           
substr($hash, 124) .
           
'-' .
           
substr($hash, 164) .
           
'-' .
           
substr($hash, 20, 12) .
           
'}';
    return
$guid;
  }
?>

tom (17-Jun-2011 08:40)

mt_rand() features in a lot of comments here. It is a good PRNG for Monte Carlo simulations, not for anything related to security. Wikipedia's page on cryptographically-secure PRNGs explains. If you want it to be hard for an attacker to guess or predict a "random" UUID, try using /dev/random instead.

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

That's obviously not production code. It's just for illustration.

Anonymous (17-Jun-2011 12:08)

Prefix can be useful, for instance, if you generate identifiers simultaneously on several hosts that might happen to generate the identifier at the same microsecond.
So we can include the hostname / servername in the id.
<?php
echo uniqid(php_uname('n'), true);
// Output: darkstar4dfa8c27aea106.40781203
?>

rommel at rommelsantor dot com (28-May-2011 11:29)

The php5-uuid functions could definitely use some documentation to clarify how they should be used, but here's what I've gleaned by examining the OSSP source code (found here: http://ossp-uuid.sourcearchive.com/documentation/1.5.1-1ubuntu1/php_2uuid_8c-source.html).

The uuid_make() function takes two arguments when generating v1 or v4, but four arguments are required when generating v3 or v5. The first two arguments have been demonstrated below and are straightforward, so I'll skip to the as-yet non-described arguments.

The third argument to uuid_make() is: $namespace
  - this is a secondary resource created with uuid_create(); it is apparently used to generate an internal UUID, which is used as the namespace of the output UUID

The fourth argument to uuid_make() is: $url
  - this is the value that is to be hashed (MD5 for v3, SHA-1 for v5); it may be any string or even null

Here's a simple class illustrating the proper usage (note that if php5-uuid is not installed on your system, each function call will just return false):

<?php
class UUID {
 
/**
   * Generates version 1: MAC address
   */
 
public static function v1() {
    if (!
function_exists('uuid_create'))
      return
false;

   
uuid_create(&$context);
   
uuid_make($context, UUID_MAKE_V1);
   
uuid_export($context, UUID_FMT_STR, &$uuid);
    return
trim($uuid);
  }

 
/**
   * Generates version 3 UUID: MD5 hash of URL
   */
 
public static function v3($i_url) {
    if (!
function_exists('uuid_create'))
      return
false;

    if (!
strlen($i_url))
     
$i_url = self::v1();

   
uuid_create(&$context);
   
uuid_create(&$namespace);

   
uuid_make($context, UUID_MAKE_V3, $namespace, $i_url);
   
uuid_export($context, UUID_FMT_STR, &$uuid);
    return
trim($uuid);
  }

 
/**
   * Generates version 4 UUID: random
   */
 
public static function v4() {
    if (!
function_exists('uuid_create'))
      return
false;

   
uuid_create(&$context);

   
uuid_make($context, UUID_MAKE_V4);
   
uuid_export($context, UUID_FMT_STR, &$uuid);
    return
trim($uuid);
  }

 
/**
   * Generates version 5 UUID: SHA-1 hash of URL
   */
 
public static function v5($i_url) {
    if (!
function_exists('uuid_create'))
      return
false;

    if (!
strlen($i_url))
     
$i_url = self::v1();

   
uuid_create(&$context);
   
uuid_create(&$namespace);

   
uuid_make($context, UUID_MAKE_V5, $namespace, $i_url);
   
uuid_export($context, UUID_FMT_STR, &$uuid);
    return
trim($uuid);
  }
}
?>

And here's a demonstration:

<?php
for ($i = 1; $i <= 3; ++$i) {
  echo
'microtime = ' . microtime(true) . '<br/>';
  echo
"V1 UUID: " . UUID::v1() . '<br/>';
  echo
"V3 UUID of URL='abc': " . UUID::v3('abc') . '<br/>';
  echo
"V4 UUID: " . UUID::v4() . '<br/>';
  echo
"V5 UUID of URL=null: " . UUID::v5(null) . '<br/>';
  echo
'<hr/>';
}
?>

And the output:

microtime = 1306620716.0457
V1 UUID: 7fddae8e-8977-11e0-bc11-003048c3b1f2
V3 UUID of URL='abc': 522ec739-ca63-3ec5-b082-08ce08ad65e2
V4 UUID: b3851ec7-4871-4527-92b5-ef5616bae1e6
V5 UUID of URL=null: e129f27c-5103-5c5c-844b-cdf0a15e160d
-------------------
microtime = 1306620716.0465
V1 UUID: 7fddb83e-8977-11e0-9e6e-003048c3b1f2
V3 UUID of URL='abc': 522ec739-ca63-3ec5-b082-08ce08ad65e2
V4 UUID: 7e78fe0d-59b8-4637-af7f-e88d221a7d1e
V5 UUID of URL=null: e129f27c-5103-5c5c-844b-cdf0a15e160d
-------------------
microtime = 1306620716.0467
V1 UUID: 7fddbfb4-8977-11e0-a2bc-003048c3b1f2
V3 UUID of URL='abc': 522ec739-ca63-3ec5-b082-08ce08ad65e2
V4 UUID: 12a940c7-0f3f-46a1-bb5f-bdd602e10654
V5 UUID of URL=null: e129f27c-5103-5c5c-844b-cdf0a15e160d

As you can see, the calls to v3() always return the same UUID because the same URL parameter, "abc", is always supplied. The same goes for the v5() function which is always supplied a null URL.

The v4() UUIDs are always entirely different because they are (pseudo)random. And the v1() calls are very similar but just slightly different because it's based on the computer's MAC address and the current time.

php at ryanmckeel dot com (29-Jun-2010 06:15)

Calls to uuid_make that use the constants UUID_MAKE_V5 or UUID_MAKE_V3 (using Debian package php5-uuid available June 2010) will not work with only two variables.

I could not find good documentation, so I read some source code and figured out that this would work:

uuid_create(&$v5);
//uuid_make($v5, UUID_MAKE_V5);
uuid_make($v5, UUID_MAKE_V5,$v5,$uniqid());
uuid_export($v5, UUID_FMT_STR, &$v5String);

Please use at your own risk.  This may not be the best way to give this param variables, but it at least makes it work what appears to be properly (generating unique ID's).

I imagine that UUID_MAKE_V3 is similar in what it needs.

  Ryan

Enrico Pallazzo (21-Mar-2010 10:45)

Running:

rand_uniqid(9007199254740989);

will return 'PpQXn7COf' and:

rand_uniqid('PpQXn7COf', true);

will return '9007199254740989'

---

If you want the rand_uniqid to be at least 6 letter long, use the $pad_up = 6 argument

---

You can support even more characters (making the resulting rand_uniqid even smaller) by adding characters to the $index var at the top of the function body.

---

<?php
function rand_uniqid($in, $to_num = false, $pad_up = false, $passKey = null)
{
   
$index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if (
$passKey !== null) {
       
// Although this function's purpose is to just make the
        // ID short - and not so much secure,
        // you can optionally supply a password to make it harder
        // to calculate the corresponding numeric ID

       
for ($n = 0; $n<strlen($index); $n++) {
           
$i[] = substr( $index,$n ,1);
        }

       
$passhash = hash('sha256',$passKey);
       
$passhash = (strlen($passhash) < strlen($index))
            ?
hash('sha512',$passKey)
            :
$passhash;

        for (
$n=0; $n < strlen($index); $n++) {
           
$p[] =  substr($passhash, $n ,1);
        }

       
array_multisort($pSORT_DESC, $i);
       
$index = implode($i);
    }

   
$base  = strlen($index);

    if (
$to_num) {
       
// Digital number  <<--  alphabet letter code
       
$in  = strrev($in);
       
$out = 0;
       
$len = strlen($in) - 1;
        for (
$t = 0; $t <= $len; $t++) {
           
$bcpow = bcpow($base, $len - $t);
           
$out   = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
        }

        if (
is_numeric($pad_up)) {
           
$pad_up--;
            if (
$pad_up > 0) {
               
$out -= pow($base, $pad_up);
            }
        }
       
$out = sprintf('%F', $out);
       
$out = substr($out, 0, strpos($out, '.'));
    } else {
       
// Digital number  -->>  alphabet letter code
       
if (is_numeric($pad_up)) {
           
$pad_up--;
            if (
$pad_up > 0) {
               
$in += pow($base, $pad_up);
            }
        }

       
$out = "";
        for (
$t = floor(log($in, $base)); $t >= 0; $t--) {
           
$bcp = bcpow($base, $t);
           
$a   = floor($in / $bcp) % $base;
           
$out = $out . substr($index, $a, 1);
           
$in  = $in - ($a * $bcp);
        }
       
$out = strrev($out); // reverse
   
}

    return
$out;
}

echo
rand_uniqid(1);
?>

Robin (04-Mar-2010 03:20)

As others below note, without prefix and without "added entropy", this function simply returns the UNIX timestamp with added microsecond counter as a hex number; it's more or less just microtime(), in hexit form.

A longer and more diverse unique id in the same hexit scheme can be achieved by instead digesting uniqid() (or a series of mt_rand(), f.e.) through md5() or sha1():

<?php

$unique
= md5( uniqid() );  // 32 characters long

$unique = sha1( uniqid() );  // 40 characters long

?>

Also worth to note is that since microtime() only works on systems that have gettimeofday() present, which Windows natively DOES NOT, uniqid() might yield just the single-second-resolution UNIX timestamp in a Windows environment.

keith at keithtyler dot com (08-Dec-2009 02:18)

For the record, the underlying function to uniqid() appears to be roughly as follows:

$m=microtime(true);
sprintf("%8x%05x\n",floor($m),($m-floor($m))*1000000);

In other words, first 8 hex chars = Unixtime, last 5 hex chars = microseconds. This is why it has microsecond precision. Also, it provides a means by which to reverse-engineer the time when a uniqid was generated:

date("r",hexdec(substr(uniqid(),0,8)));

Increasingly as you go further down the string, the number becomes "more unique" over time, with the exception of digit 9, where numeral prevalence is 0..3>4>5..f, because of the difference between 10^6 and 16^5 (this is presumably true for the remaining digits as well but much less noticeable).

Andrew Moore (04-Dec-2009 04:45)

The following class generates VALID RFC 4211 COMPLIANT Universally Unique IDentifiers (UUID) version 3, 4 and 5.

Version 3 and 5 UUIDs are named based. They require a namespace (another valid UUID) and a value (the name). Given the same namespace and name, the output is always the same.

Version 4 UUIDs are pseudo-random.

UUIDs generated below validates using OSSP UUID Tool, and output for named-based UUIDs are exactly the same. This is a pure PHP implementation.

<?php
class UUID {
  public static function
v3($namespace, $name) {
    if(!
self::is_valid($namespace)) return false;

   
// Get hexadecimal components of namespace
   
$nhex = str_replace(array('-','{','}'), '', $namespace);

   
// Binary Value
   
$nstr = '';

   
// Convert Namespace UUID to bits
   
for($i = 0; $i < strlen($nhex); $i+=2) {
     
$nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
    }

   
// Calculate hash value
   
$hash = md5($nstr . $name);

    return
sprintf('%08s-%04s-%04x-%04x-%12s',

     
// 32 bits for "time_low"
     
substr($hash, 0, 8),

     
// 16 bits for "time_mid"
     
substr($hash, 8, 4),

     
// 16 bits for "time_hi_and_version",
      // four most significant bits holds version number 3
     
(hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000,

     
// 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
     
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,

     
// 48 bits for "node"
     
substr($hash, 20, 12)
    );
  }

  public static function
v4() {
    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)
    );
  }

  public static function
v5($namespace, $name) {
    if(!
self::is_valid($namespace)) return false;

   
// Get hexadecimal components of namespace
   
$nhex = str_replace(array('-','{','}'), '', $namespace);

   
// Binary Value
   
$nstr = '';

   
// Convert Namespace UUID to bits
   
for($i = 0; $i < strlen($nhex); $i+=2) {
     
$nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
    }

   
// Calculate hash value
   
$hash = sha1($nstr . $name);

    return
sprintf('%08s-%04s-%04x-%04x-%12s',

     
// 32 bits for "time_low"
     
substr($hash, 0, 8),

     
// 16 bits for "time_mid"
     
substr($hash, 8, 4),

     
// 16 bits for "time_hi_and_version",
      // four most significant bits holds version number 5
     
(hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x5000,

     
// 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
     
(hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,

     
// 48 bits for "node"
     
substr($hash, 20, 12)
    );
  }

  public static function
is_valid($uuid) {
    return
preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?'.
                     
'[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) === 1;
  }
}

// Usage
// Named-based UUID.

$v3uuid = UUID::v3('1546058f-5a25-4334-85ae-e68f2a44bbaf', 'SomeRandomString');
$v5uuid = UUID::v5('1546058f-5a25-4334-85ae-e68f2a44bbaf', 'SomeRandomString');

// Pseudo-random UUID

$v4uuid = UUID::v4();
?>

John Haugeland from FullOf.BS (26-May-2009 06:11)

Generating an MD5 from a unique ID is naive and reduces much of the value of unique IDs, as well as providing significant (attackable) stricture on the MD5 domain.  That's a deeply broken thing to do.  The correct approach is to use the unique ID on its own; it's already geared for non-collision.

IDs should never be obfuscated for security, so if you're worried about someone guessing your ID, fix the system, don't just make it harder to guess (because it's nowhere near as difficult to guess as you imagine: you can just brute force the 60,000 MD5s that are generatable from millisecond IDs over the course of a given minute, which the typical computer can do in less than 0.1s).

If you absolutely need to involve a hash somehow - maybe to placate a boss who thinks they understand security much better than they actually do - append it instead.

function BadIdeaID() { return uniqid() . '_' . md5(mt_rand()); }

Marius Karthaus (23-Jan-2009 07:10)

Better yet, just use the php5-uuid extension and this class to create 'official' UUIDs at high speed (On my system 1000 UUIDs are created in 0.0064 seconds)

<?php

Usage
:
$uuid=new uuid();
echo
$uuid->v1();

class
uuid {
   
/**
     * This class enables you to get real uuids using the OSSP library.
     * Note you need php-uuid installed.
     * On my system 1000 UUIDs are created in 0.0064 seconds.
     *
     * @author Marius Karthaus
     *
     */
   
   
protected $uuidobject;
   
   
/**
     * On long running deamons i've seen a lost resource. This checks the resource and creates it if needed.
     *
     */
   
protected function create() {
        if (!
is_resource ( $this->uuidobject )) {
           
uuid_create ( &$this->uuidobject );
        }
    }
   
   
/**
     * Return a type 1 (MAC address and time based) uuid
     *
     * @return String
     */
   
public function v1() {
       
$this->create ();
       
uuid_make ( $this->uuidobject, UUID_MAKE_V1 );
       
uuid_export ( $this->uuidobject, UUID_FMT_STR, &$uuidstring );
        return
trim ( $uuidstring );
    }
   
   
/**
     * Return a type 4 (random) uuid
     *
     * @return String
     */
   
public function v4() {
       
$this->create ();
       
uuid_make ( $this->uuidobject, UUID_MAKE_V4 );
       
uuid_export ( $this->uuidobject, UUID_FMT_STR, &$uuidstring );
        return
trim ( $uuidstring );
    }
   
   
/**
     * Return a type 5 (SHA-1 hash) uuid
     *
     * @return String
     */
   
public function v5() {
       
$this->create ();
       
uuid_make ( $this->uuidobject, UUID_MAKE_V5 );
       
uuid_export ( $this->uuidobject, UUID_FMT_STR, &$uuidstring );
        return
trim ( $uuidstring );
    }
}
?>

Marius Karthaus (22-Jan-2009 01:39)

I've created a class from the function below. With it, creating a lot of uuids beomes about 100 times faster because you do not need to fopen() for each uuid.
uuid::get() still works as a static function if you just need a single uuid.

<?php
class uuid {
   
    protected
$urand;
   
    public function
__construct() {
       
$this->urand = @fopen ( '/dev/urandom', 'rb' );
    }

   
/**
     * @brief Generates a Universally Unique IDentifier, version 4.
     *
     * This function generates a truly random UUID. The built in CakePHP String::uuid() function
     * is not cryptographically secure. You should uses this function instead.
     *
     * @see http://tools.ietf.org/html/rfc4122#section-4.4
     * @see http://en.wikipedia.org/wiki/UUID
     * @return string A UUID, made up of 32 hex digits and 4 hyphens.
     */
   
function get() {
       
       
$pr_bits = false;
        if (
is_a ( $this, 'uuid' )) {
            if (
is_resource ( $this->urand )) {
               
$pr_bits .= @fread ( $this->urand, 16 );
            }
        }
        if (!
$pr_bits) {
           
$fp = @fopen ( '/dev/urandom', 'rb' );
            if (
$fp !== false) {
               
$pr_bits .= @fread ( $fp, 16 );
                @
fclose ( $fp );
            } else {
               
// If /dev/urandom isn't available (eg: in non-unix systems), use mt_rand().
               
$pr_bits = "";
                for(
$cnt = 0; $cnt < 16; $cnt ++) {
                   
$pr_bits .= chr ( mt_rand ( 0, 255 ) );
                }
            }
        }
       
$time_low = bin2hex ( substr ( $pr_bits, 0, 4 ) );
       
$time_mid = bin2hex ( substr ( $pr_bits, 4, 2 ) );
       
$time_hi_and_version = bin2hex ( substr ( $pr_bits, 6, 2 ) );
       
$clock_seq_hi_and_reserved = bin2hex ( substr ( $pr_bits, 8, 2 ) );
       
$node = bin2hex ( substr ( $pr_bits, 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 );
    }

}

sean at seancolombo dot com (06-Jan-2009 03:56)

This builds slightly on david's post below.  The differences are that it doesn't require Cake anymore and there is a graceful fallback for /dev/urandom in case that isn't available (/dev/urandom is not available on windows systems for example).

Since it uses mt_rand(), it is still cryptographically secure.

<?php
/**
     * @brief Generates a Universally Unique IDentifier, version 4.
     *
     * This function generates a truly random UUID. The built in CakePHP String::uuid() function
     * is not cryptographically secure. You should uses this function instead.
     *
     * @see http://tools.ietf.org/html/rfc4122#section-4.4
     * @see http://en.wikipedia.org/wiki/UUID
     * @return string A UUID, made up of 32 hex digits and 4 hyphens.
     */
     
function uuidSecure() {
      
       
$pr_bits = null;
       
$fp = @fopen('/dev/urandom','rb');
        if (
$fp !== false) {
           
$pr_bits .= @fread($fp, 16);
            @
fclose($fp);
        } else {
           
// If /dev/urandom isn't available (eg: in non-unix systems), use mt_rand().
           
$pr_bits = "";
            for(
$cnt=0; $cnt < 16; $cnt++){
               
$pr_bits .= chr(mt_rand(0, 255));
            }
        }
      
       
$time_low = bin2hex(substr($pr_bits,0, 4));
       
$time_mid = bin2hex(substr($pr_bits,4, 2));
       
$time_hi_and_version = bin2hex(substr($pr_bits,6, 2));
       
$clock_seq_hi_and_reserved = bin2hex(substr($pr_bits,8, 2));
       
$node = bin2hex(substr($pr_bits,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);
    }

david at ramaboo dot com (05-Jan-2009 08:51)

Another UUID function. This time using /dev/random
<?php
/**
     * @brief Generates a Universally Unique IDentifier, version 4.
     *
     * This function generates a truly random UUID. The built in CakePHP String::uuid() function
     * is not cryptographically secure. You should uses this function instead.
     *
     * @see http://tools.ietf.org/html/rfc4122#section-4.4
     * @see http://en.wikipedia.org/wiki/UUID
     * @return string A UUID, made up of 32 hex digits and 4 hyphens.
     */
   
public function uuidSecure() {
       
       
$pr_bits = null;
       
$fp = @fopen('/dev/urandom','rb');
        if (
$fp !== false) {
           
$pr_bits .= @fread($fp, 16);
            @
fclose($fp);
        } else {
           
$this->cakeError('randomNumber');
        }
       
       
$time_low = bin2hex(substr($pr_bits,0, 4));
       
$time_mid = bin2hex(substr($pr_bits,4, 2));
       
$time_hi_and_version = bin2hex(substr($pr_bits,6, 2));
       
$clock_seq_hi_and_reserved = bin2hex(substr($pr_bits,8, 2));
       
$node = bin2hex(substr($pr_bits,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);
    }
?>

valerylourie at gmail dot com (13-Aug-2008 09:38)

To dholmes at cfdsoftware dot net:
sprintf parameter should be changed to '%04x%04x-%04x-4%03x-%04x-%04x%04x%04x' - 4 in GUID V4 should be first, but not last digit in the third group

lucas at paivlek dot net (12-Jul-2008 05:07)

Hi all,

I'm not sure what performance problems you guys are talking about, but I benchmarked 10000 of each of the functions outlined in this comments section alongside the uniqid method. Where function names are duplicated (because everyone likes to call their method uuid), i've added part of that contributer's name to the function name.

So if you want something unique and you think that uniqid() is slowing your scripts down, maybe rand() is your only answer :)

See the output (quickest to slowest):
Array
(
    [0] => Array
        (
            [strMethod] => rand
            [intRepetitions] => 10000
            [flTotalTime] => 0.104878902435
            [flAverageTime] => 1.04878902435E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000297069549561
            [exampleOutput] => 19689
        )

    [1] => Array
        (
            [strMethod] => uniqid
            [intRepetitions] => 10000
            [flTotalTime] => 0.123727083206
            [flAverageTime] => 1.23727083206E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 9.3936920166E-5
            [exampleOutput] => 48782b3c3e997
        )

    [2] => Array
        (
            [strMethod] => uniqid2
            [intRepetitions] => 10000
            [flTotalTime] => 0.132393836975
            [flAverageTime] => 1.32393836975E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000135898590088
            [exampleOutput] => 48782b3d282c
        )

    [3] => Array
        (
            [strMethod] => uuid_mimec
            [intRepetitions] => 10000
            [flTotalTime] => 0.192065000534
            [flAverageTime] => 1.92065000534E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000221014022827
            [exampleOutput] => fc1b24b7-59e0-4935-88f2-06a6fde922d8
        )

    [4] => Array
        (
            [strMethod] => uuid_maciej
            [intRepetitions] => 10000
            [flTotalTime] => 0.219671964645
            [flAverageTime] => 2.19671964645E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000162839889526
            [exampleOutput] => 4c55bc99-b5bc-0f88-0387-00001a51bd33
        )

    [5] => Array
        (
            [strMethod] => uuid_dholmes
            [intRepetitions] => 10000
            [flTotalTime] => 0.221670866013
            [flAverageTime] => 2.21670866013E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000198125839233
            [exampleOutput] => cd79ac78-af08-a804-6567-3c4940e3252b
        )

    [6] => Array
        (
            [strMethod] => newid
            [intRepetitions] => 10000
            [flTotalTime] => 0.221807956696
            [flAverageTime] => 2.21807956696E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000211954116821
            [exampleOutput] => user_9e2e305b4c4340397617baf285f11eb348782b3d0354b3.
095706961215834941127.0.0.1
        )

    [7] => Array
        (
            [strMethod] => uuid_nodkz
            [intRepetitions] => 10000
            [flTotalTime] => 0.477306842804
            [flAverageTime] => 4.77306842804E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.000205039978027
            [exampleOutput] => 0001-7f000001-48782b3e-0952-ef8e7ec8
        )

    [8] => Array
        (
            [strMethod] => uuid_lance
            [intRepetitions] => 10000
            [flTotalTime] => 0.668512821198
            [flAverageTime] => 6.68512821198E-5
            [flShortestTime] => 3.81469726562E-6
            [flLongestTime] => 0.00036096572876
            [exampleOutput] => fa465717-26bf-4af9-987f-33bb54905678
        )

)

mark at whytewaters dot com (26-Feb-2008 02:39)

If you want many ids and performance of this function is an issue why not pull uniquid() out of the loop, eg:

$base = uniqueid();
$ids[] = array();

for ($index = 0; $index < 100000; $index++) 
    $ids[] = $base . '.' . $index;

nodkz at mail dot ru (15-Jan-2008 10:00)

I use such UUID (it not RFC!!!)
(server_id)-(clientIP)-(unixtime)-(milliseconds)-(random)

 I can easyly determine which server at which time and who initiate creating of object.

<?php

$u
=uuid();   // 0001-7f000001-478c8000-4801-47242987
echo $u;
echo
"<br>";
print_r(uuidDecode($u)); // Array ( [serverID] => 0001 [ip] => 127.0.0.1 [unixtime] => 1200390144 [micro] => 0.28126525878906 )

function uuid($serverID=1)
{
   
$t=explode(" ",microtime());
    return
sprintf( '%04x-%08s-%08s-%04s-%04x%04x',
       
$serverID,
       
clientIPToHex(),
       
substr("00000000".dechex($t[1]),-8),   // get 8HEX of unixtime
       
substr("0000".dechex(round($t[0]*65536)),-4), // get 4HEX of microtime
       
mt_rand(0,0xffff), mt_rand(0,0xffff));
}

function
uuidDecode($uuid) {
   
$rez=Array();
   
$u=explode("-",$uuid);
    if(
is_array($u)&&count($u)==5) {
       
$rez=Array(
           
'serverID'=>$u[0],
           
'ip'=>clientIPFromHex($u[1]),
           
'unixtime'=>hexdec($u[2]),
           
'micro'=>(hexdec($u[3])/65536)
        );
    }
    return
$rez;
}

function
clientIPToHex($ip="") {
   
$hex="";
    if(
$ip=="") $ip=getEnv("REMOTE_ADDR");
   
$part=explode('.', $ip);
    for (
$i=0; $i<=count($part)-1; $i++) {
       
$hex.=substr("0".dechex($part[$i]),-2);
    }
    return
$hex;
}

function
clientIPFromHex($hex) {
   
$ip="";
    if(
strlen($hex)==8) {
       
$ip.=hexdec(substr($hex,0,2)).".";
       
$ip.=hexdec(substr($hex,2,2)).".";
       
$ip.=hexdec(substr($hex,4,2)).".";
       
$ip.=hexdec(substr($hex,6,2));
    }
    return
$ip;
}

?>

smp_info at yahoo dot com (06-Sep-2007 01:38)

This function is painfully slow if you're using it to give images random names inside of a loop.  The following function will give you a random name *every* time and is much faster.

<?php

function nameImage($imgExtension)
{
    return
time() . substr(md5(microtime()), 0, rand(5, 12)) . $imgExtension;
}

?>

dot dot dot dot dot alexander at gmail dot com (13-Jun-2007 10:28)

I use this mangle currently:
( inserts the IP, uses time() and a prefix, aside the uniqid)

<?php
if(!function_exists("newid")){

    function
newid($prefix = "user_"){
        return (
$prefix . uniqid( hash("md5", time()), TRUE ) . time() . @$_SERVER['REMOTE_ADDR']);
    }
/* endfunction */

}/* endif  */
?>

ken at smallboxsoftware (17-May-2007 05:34)

Just to note this function is fairly slow, and can bring your script to a crawl if it is in a loop. Strangely if you run it as uniqid('', true) it runs much more quickly

rjchallen at gmail dot com (17-May-2007 04:08)

If you can guarantee a connection to mysql when you need your UUID then you can wrap up MySQL's (v5+) function.

function uuid() {
    return mysql_result(mysql_query('Select UUID()'),0);
}

kristoffer dot paro at gmail dot com (13-May-2007 04:05)

In response to the notes about UUID generation added by mimec and lance_rushing at hotmail dot com.

Calling mt_rand the fewest possible times is not necessarily the fastest, if it heavily utilizes string handling routines. I did a quick benchmark between the two functions and discovered that lance's function (using only 5 mt_rands) was about 6.5 times _slower_ than mimec's on my system.

Jason (27-Mar-2007 08:10)

Neither the pseudo-random number rand() nor the Mersenne Twister algorithms are cryptographically strong, and this is well known.  Simply combining non-cryptographically strong algorithms doesn't not make a cryptographically strong algorithm either.  Mersenne Twister is a fast algorithm with good k-distribution which will give you numbers for a long time before it repeats itself.  MT, rand(), and MD5 should NOT be used for encryption, or for cookies that that store a session ID that gives personal information.  A simple application where non-collision of session IDs is highly preferred but not critical, such as storing a user's shopping cart items for when they return to your site (but not their personal information), IS a good use for the MT, rand() MD5, uniqid() and combinations thereof.

mailrinke at _cutthis_yahoo dot com (19-Feb-2007 01:06)

I have been using mimecs version lately and do not think it's safe to think the results are always unqiue.

Although it could be just my bad programming, I found exactly 1 collission while debugging my code. It seems to me that if my code was incorrect it would have happened more than once.

I recommend anyone to include time as a factor of such an ID as to be a little more certain it is in fact unique.

Emery (31-Jan-2007 08:13)

The example given in this document for a "better token" should be:

<?php
$better_token
= uniqid(md5(rand()), true);
?>

As it is now, the result isn't guaranteed to be unique, because MD5 has collisions.

lance_rushing at hotmail dot com (23-Jan-2007 08:43)

wooshoofoo, the reason mimec is calling mt_rand multiple times is because the largest number mt_rand can produce is 2^31 (2147483647, as reported by mt_getrandmax() on my server).  RFC 4122 requires a 128 bit value.

Also they are not "4 digit sequeces", but 4 digit hexadecimal numbers.  16^4 == 2^16.

mimec's limiting each random result to 2^16 avoids problem of PHP's 2^32 integer max (http://php.net/manual/en/language.types.integer.php).

If you want to call mt_rand fewer times:  mimec's version calls mt_rand 8 times ( 16 bits * 8 = 128 bits ).  You *could* call mt_rand 5 times ( 31 bits + 31 bits + 31 bits + 31 bits + 4 bits = 128 bits ).  But then you would have keep all your values as strings.

Something like:

<?php
/**
 * Another (ugly) "random or pseudo-random" version of RFC 4122
 *
 * This version calls mt_rand() the fewest possible times.
 * if mt_getrandmax() == 2^31 then this will call mt_rand() 5 times YMMV
 *
 * Personally, I would use mimec's version
 * To handle the large values, we'll keep everything as strings.
 *
 * @return string
 */
function uuid() {   

    
// Generate 128 bit random sequence
    
$randmax_bits = strlen(base_convert(mt_getrandmax(), 10, 2));  // how many bits is mt_getrandmax()
    
$x = '';
     while (
strlen($x) < 128) {
        
$maxbits = (128 - strlen($x) < $randmax_bits) ? 128 - strlen($x) :  $randmax_bits;
        
$x .= str_pad(base_convert(mt_rand(0, pow(2,$maxbits)), 10, 2), $maxbits, "0", STR_PAD_LEFT);
     }

    
// break into fields
    
$a = array();
    
$a['time_low_part'] = substr($x, 0, 32);
    
$a['time_mid'] = substr($x, 32, 16);
    
$a['time_hi_and_version'] = substr($x, 48, 16);
    
$a['clock_seq'] = substr($x, 64, 16);
    
$a['node_part'] =  substr($x, 80, 48);
    
    
// Apply bit masks for "random or pseudo-random" version per RFC
    
$a['time_hi_and_version'] = substr_replace($a['time_hi_and_version'], '0100', 0, 4);
    
$a['clock_seq'] = substr_replace($a['clock_seq'], '10', 0, 2);

   
// Format output
   
return sprintf('%s-%s-%s-%s-%s',
       
str_pad(base_convert($a['time_low_part'], 2, 16), 8, "0", STR_PAD_LEFT),
       
str_pad(base_convert($a['time_mid'], 2, 16), 4, "0", STR_PAD_LEFT),
       
str_pad(base_convert($a['time_hi_and_version'], 2, 16), 4, "0", STR_PAD_LEFT),
       
str_pad(base_convert($a['clock_seq'], 2, 16), 4, "0", STR_PAD_LEFT),
       
str_pad(base_convert($a['node_part'], 2, 16), 12, "0", STR_PAD_LEFT));
}

?>

However, I think mimec's version is much more elegant.

mailbox1 at highhost dot net (23-Dec-2006 12:22)

Also you may use this if you like it.

function uniqid2() {
return dechex(time()).dechex(mt_rand(1,65535));
}

wooshoofoo (07-Dec-2006 02:00)

I'm not sure the previous function by mimec is really all that random.  For one thing, generating 8 small random 4 digit sequeces != generating one 32 digit sequence.

mimec (25-Aug-2006 09:36)

Here is the correct version of a function generating a pseudo-random UUID according to RFC 4122:

<?php

function uuid()
{
    return
sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
       
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
       
mt_rand( 0, 0x0fff ) | 0x4000,
       
mt_rand( 0, 0x3fff ) | 0x8000,
       
mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ) );
}

?>

The version and variant is located at the MSB (most significant bits) of the time_hi_and_version and clock_seq_hi_and_reserved fields, not the LSB as in dholmes version.

admin at code-dynasty dot net (09-Jul-2006 04:46)

I'm not too fond of the recommendation to use an MD5 of the unique ID for session IDs. It would be a better idea just to use uniqueid(rand(), true) without the MD5, because even though it's a rare circumstance, MD5 is a hash, not an encryption, which means it has collisions. Therefore you theoretically could have multiple users given the same session ID which could result in one user's ability to access another user's data.

dholmes at cfdsoftware dot net (09-May-2006 04:26)

WARNING : I believe there are a couple of mistakes in the function provided just below by maciej dot strzelecki at gmail dot com. Namely, that in the two substr_replace() calls, the third parameters should respectively be 12 (instead of 11) and 6 (instead of 5).

Considering the importance of this function, I went to read RFC 4122 myself, and found the discrepancy. I therefore chose to write my own function, inspired by the previous one, but with a few enhancements detailed in the comments. On the downside, it might be slightly less easy to understand at first glance.

Please feel free to use it yourself. Thank you also in advance for any feedback at dholmes at cfdsoftware.net .

<?php

/**
 * Generates a Universally Unique IDentifier, version 4.
 *
 * RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt) defines a special type of Globally
 * Unique IDentifiers (GUID), as well as several methods for producing them. One
 * such method, described in section 4.4, is based on truly random or pseudo-random
 * number generators, and is therefore implementable in a language like PHP.
 *
 * We choose to produce pseudo-random numbers with the Mersenne Twister, and to always
 * limit single generated numbers to 16 bits (ie. the decimal value 65535). That is
 * because, even on 32-bit systems, PHP's RAND_MAX will often be the maximum *signed*
 * value, with only the equivalent of 31 significant bits. Producing two 16-bit random
 * numbers to make up a 32-bit one is less efficient, but guarantees that all 32 bits
 * are random.
 *
 * The algorithm for version 4 UUIDs (ie. those based on random number generators)
 * states that all 128 bits separated into the various fields (32 bits, 16 bits, 16 bits,
 * 8 bits and 8 bits, 48 bits) should be random, except : (a) the version number should
 * be the last 4 bits in the 3rd field, and (b) bits 6 and 7 of the 4th field should
 * be 01. We try to conform to that definition as efficiently as possible, generating
 * smaller values where possible, and minimizing the number of base conversions.
 *
 * @copyright   Copyright (c) CFD Labs, 2006. This function may be used freely for
 *              any purpose ; it is distributed without any form of warranty whatsoever.
 * @author      David Holmes <dholmes@cfdsoftware.net>
 *
 * @return  string  A UUID, made up of 32 hex digits and 4 hyphens.
 */

function uuid() {
   
   
// The field names refer to RFC 4122 section 4.1.2

   
return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x',
       
mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low"
       
mt_rand(0, 65535), // 16 bits for "time_mid"
       
mt_rand(0, 4095),  // 12 bits before the 0100 of (version) 4 for "time_hi_and_version"
       
bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)),
           
// 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res"
            // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d)
            // 8 bits for "clk_seq_low"
       
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node" 
   
); 
}

?>

maciej dot strzelecki at gmail dot com (16-Apr-2006 07:09)

This is an implementation of version 4 UUID, which is generating UUIDs from truly-random numbers.

<?php
/* Copyright 2006 Maciej Strzelecki

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

function uuid()
{
   
// version 4 UUID
   
return sprintf(
       
'%08x-%04x-%04x-%02x%02x-%012x',
       
mt_rand(),
       
mt_rand(0, 65535),
       
bindec(substr_replace(
           
sprintf('%016b', mt_rand(0, 65535)), '0100', 11, 4)
        ),
       
bindec(substr_replace(sprintf('%08b', mt_rand(0, 255)), '01', 5, 2)),
       
mt_rand(0, 255),
       
mt_rand()
    );
}
?>