php中的短唯一ID

时间:2008-11-21 01:05:58

标签: php uniqueidentifier unique-index

我想创建一个唯一ID,但uniqid()正在提供类似'492607b0ee414'的内容。我想要的是类似于tinyurl给出的东西:'64k8ra'。越短越好。唯一的要求是它不应该有明显的顺序,它应该看起来比看似随机的数字序列更漂亮。字母比数字更受欢迎,理想情况下不会是混合字母。由于参赛人数不会很多(最多10000人左右),因此碰撞的风险并不是一个很大的因素。

任何建议表示赞赏。

16 个答案:

答案 0 :(得分:42)

创建一个小函数,返回给定长度的随机字母:

<?php
function generate_random_letters($length) {
    $random = '';
    for ($i = 0; $i < $length; $i++) {
        $random .= chr(rand(ord('a'), ord('z')));
    }
    return $random;
}

然后你会想要用伪代码来调用它,直到它是唯一的,这取决于你存储信息的位置:

do {
    $unique = generate_random_letters(6);
} while (is_in_table($unique));
add_to_table($unique);

您可能还想确保字母不会在字典中形成单词。可能是整个英语词典或只是一个坏词词,以避免客户发现的不良品味。

编辑:如果您打算使用它,那么我也会添加这个意义,但这不是大量的项目,因为您获得的冲突越多(在表中已经获得ID),这可能会变得非常慢。当然,您需要一个索引表,并且您需要调整ID中的字母数以避免冲突。在这种情况下,使用6个字母,您将有26 ^ 6 = 308915776个可能的唯一ID(减去坏词),这足以满足您的需要10000个。

编辑: 如果您想要字母和数字的组合,可以使用以下代码:

$random .= rand(0, 1) ? rand(0, 9) : chr(rand(ord('a'), ord('z')));

答案 1 :(得分:27)

@gen_uuid()由gord。

preg_replace遇到了一些令人讨厌的utf-8问题,导致uid somtimes包含“+”或“/”。 要解决这个问题,你必须明确地制作模式utf-8

function gen_uuid($len=8) {

    $hex = md5("yourSaltHere" . uniqid("", true));

    $pack = pack('H*', $hex);
    $tmp =  base64_encode($pack);

    $uid = preg_replace("#(*UTF8)[^A-Za-z0-9]#", "", $tmp);

    $len = max(4, min(128, $len));

    while (strlen($uid) < $len)
        $uid .= gen_uuid(22);

    return substr($uid, 0, $len);
}

我花了很长时间才发现,也许这让别人感到头疼

答案 2 :(得分:24)

您可以使用更少的代码实现这一目标:

function gen_uid($l=10){
    return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}

结果(例子):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

答案 3 :(得分:17)

有两种方法可以获得可靠的唯一ID:使其变得如此长且可变,以至于碰撞的可能性非常小(与GUID一样)或将所有生成的ID存储在表中以供查找(在内存中或在数据库或文件中)以验证生成时的唯一性。

如果您真的问如何生成这样一个短密钥并保证其独特性而不进行某种重复检查,答案是,您不能。

答案 4 :(得分:11)

这是我用于任意长度的随机base62s的例程...

调用gen_uuid()会返回WJX0u0jV, E9EMaZ3P等字符串

默认情况下,它返回8位数,因此空间为64 ^ 8或大约10 ^ 14, 这通常足以使碰撞非常罕见。

对于更大或更小的字符串,根据需要传入$ len。没有限制长度,因为我追加到[满足128个字符的安全限制,可以删除]。

注意,在md5中使用随机盐 [如果您愿意,可以使用sha1],因此不能轻易进行逆向工程。

我在网上找不到任何可靠的base62转换,因此这种方法从base64结果中删除了chars。

在BSD许可下自由使用, 享受,

gord

function gen_uuid($len=8)
{
    $hex = md5("your_random_salt_here_31415" . uniqid("", true));

    $pack = pack('H*', $hex);

    $uid = base64_encode($pack);        // max 22 chars

    $uid = ereg_replace("[^A-Za-z0-9]", "", $uid);    // mixed case
    //$uid = ereg_replace("[^A-Z0-9]", "", strtoupper($uid));    // uppercase only

    if ($len<4)
        $len=4;
    if ($len>128)
        $len=128;                       // prevent silliness, can remove

    while (strlen($uid)<$len)
        $uid = $uid . gen_uuid(22);     // append until length achieved

    return substr($uid, 0, $len);
}

答案 5 :(得分:11)

非常简单的解决方案:

使用以下内容制作唯一ID:

$id = 100;
base_convert($id, 10, 36);

再次获取原始值:

intval($str,36);

不能因为它来自另一个堆栈溢出页面而受到赞誉,但我认为解决方案非常优雅且非常棒,以至于值得复制到此线程以供引用此功能的人使用。

答案 6 :(得分:4)

如果要来回转换,可以使用Id并将其转换为base-36数字。可用于任何具有整数id的表。

function toUId($baseId, $multiplier = 1) {
    return base_convert($baseId * $multiplier, 10, 36);
}
function fromUId($uid, $multiplier = 1) {
    return (int) base_convert($uid, 36, 10) / $multiplier;
}

echo toUId(10000, 11111);
1u5h0w
echo fromUId('1u5h0w', 11111);
10000

聪明的人可能会用足够的id示例来解决它。不要让这种默默无闻取代安全。

答案 7 :(得分:4)

我想出了一个非常酷的解决方案,没有进行唯一性检查。我以为我会为未来的访客分享。

计数器是保证唯一性的一种非常简单的方法,或者如果您使用数据库,主键也可以保证唯一性。问题是它看起来很糟糕,而且可能很脆弱。所以我接受了序列并用密码混淆了它。由于密码可以反转,我知道每个id都是唯一的,但仍然是随机出现的。

这是python而不是php,但我在这里上传了代码: https://github.com/adecker89/Tiny-Unique-Identifiers

答案 8 :(得分:3)

字母很漂亮,数字很难看。 你想要随机字符串,但不想要“丑陋”的随机字符串?

创建一个随机数并以 alpha-style base-26 )打印,就像航空公司提供的预订“数字”一样。

据我所知,PHP中没有通用的基本转换函数,所以你需要自己编写代码。

另一种选择:使用uniqid()并删除数字。

function strip_digits_from_string($string) {
    return preg_replace('/[0-9]/', '', $string);
}

或者用字母替换它们:

function replace_digits_with_letters($string) {
    return strtr($string, '0123456789', 'abcdefghij');
}

答案 9 :(得分:1)

你也可以这样做:

public static function generateCode($length = 6)
    {
        $az = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $azr = rand(0, 51);
        $azs = substr($az, $azr, 10);
        $stamp = hash('sha256', time());
        $mt = hash('sha256', mt_rand(5, 20));
        $alpha = hash('sha256', $azs);
        $hash = str_shuffle($stamp . $mt . $alpha);
        $code = ucfirst(substr($hash, $azr, $length));
        return $code;
    }

答案 10 :(得分:1)

你可以用干净,易于阅读的方式做而不用不洁/宇宙的东西,比如循环,字符串连接或多次调用rand()。此外,最好使用mt_rand()

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    return dechex($random);
}

如果您需要String在任何情况下具有确切的长度,只需用零填充十六进制数字:

function createRandomString($length)
{
    $random = mt_rand(0, (1 << ($length << 2)) - 1);
    $number = dechex($random);
    return str_pad($number, $length, '0', STR_PAD_LEFT);
}

“理论上的反向”是,你仅限于PHP的功能 - 但在这种情况下这更像是一个哲学问题;)无论如何我们都要经历它:

  • PHP的限制在于它可以表示为十六进制数字。在32位系统上,这将是$length <= 8 至少,其中PHP的限制应为4.294.967.295。
  • PHP随机数生成器也有最大值。对于32位系统上的mt_rand() 至少,它应该是2.147.483.647
  • 因此,理论上您只能使用2.147.483.647 ID。

回到主题 - 直观do { (generate ID) } while { (id is not uniqe) } (insert id)有一个缺点,一个可能的缺陷可能会让你直接走向黑暗......

缺点:验证是悲观的。像这样总是这样做需要检查数据库。拥有足够的密钥空间(例如,10k条目的长度为5)将不太可能经常发生冲突,因为它可能相对消耗更少的资源而只是尝试存储数据并仅在以下情况下重试一个UNIQUE KEY错误。

缺陷: 用户A 检索已验证为尚未使用的ID。然后代码将尝试插入数据。但与此同时,用户B 进入相同的循环并且不幸地检索到相同的随机数,因为用户A 尚未存储且此ID仍然是免费的。现在系统存储用户B 用户A ,并且在尝试存储第二个用户时,同时已经存在另一个用户 - 具有相同的ID。< / p>

在任何情况下都需要处理该异常,并且需要使用新创建的ID重新尝试插入。在保持悲观检查循环(您需要重新输入)的同时添加它将导致非常难看且难以遵循的代码。 幸运的是,解决这个问题的方法与缺点相同:首先去做它并尝试存储数据。如果出现UNIQUE KEY错误,只需使用新ID重试。

答案 11 :(得分:1)

看看这篇文章

它解释了如何从你的bdd id中生成短的唯一ID,就像youtube一样。

实际上,文章中的函数与php function base_convert非常相关,{{3}}将数字从基数转换为另一个(但最多只能基数为36)。

答案 12 :(得分:0)

function rand_str($len = 12, $type = '111', $add = null) {
    $rand = ($type[0] == '1'  ? 'abcdefghijklmnpqrstuvwxyz' : '') .
            ($type[1] == '1'  ? 'ABCDEFGHIJKLMNPQRSTUVWXYZ' : '') .
            ($type[2] == '1'  ? '123456789'                 : '') .
            (strlen($add) > 0 ? $add                        : '');

    if(empty($rand)) $rand = sha1( uniqid(mt_rand(), true) . uniqid( uniqid(mt_rand(), true), true) );

    return substr(str_shuffle( str_repeat($rand, 2) ), 0, $len);
}

答案 13 :(得分:0)

如果您确实喜欢更长版本的唯一ID,请使用此选项:
$ uniqueid = sha1(md5(time()));

答案 14 :(得分:0)

最佳答案:Smallest Unique "Hash Like" String Given Unique Database ID - PHP Solution, No Third Party Libraries Required.

以下是代码:

<?php
/*
THE FOLLOWING CODE WILL PRINT:
A database_id value of 200 maps to 5K
A database_id value of 1 maps to 1
A database_id value of 1987645 maps to 16LOD
*/
$database_id = 200;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 200 maps to $base36value\n";
$database_id = 1;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1 maps to $base36value\n";
$database_id = 1987645;
$base36value = dec2string($database_id, 36);
echo "A database_id value of 1987645 maps to $base36value\n";

// HERE'S THE FUNCTION THAT DOES THE HEAVY LIFTING...
function dec2string ($decimal, $base)
// convert a decimal number into a string using $base
{
    //DebugBreak();
   global $error;
   $string = null;

   $base = (int)$base;
   if ($base < 2 | $base > 36 | $base == 10) {
      echo 'BASE must be in the range 2-9 or 11-36';
      exit;
   } // if

   // maximum character string is 36 characters
   $charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';

   // strip off excess characters (anything beyond $base)
   $charset = substr($charset, 0, $base);

   if (!ereg('(^[0-9]{1,50}$)', trim($decimal))) {
      $error['dec_input'] = 'Value must be a positive integer with < 50 digits';
      return false;
   } // if

   do {
      // get remainder after dividing by BASE
      $remainder = bcmod($decimal, $base);

      $char      = substr($charset, $remainder, 1);   // get CHAR from array
      $string    = "$char$string";                    // prepend to output

      //$decimal   = ($decimal - $remainder) / $base;
      $decimal   = bcdiv(bcsub($decimal, $remainder), $base);

   } while ($decimal > 0);

   return $string;

}

?>

答案 15 :(得分:0)

10个字符:

substr(uniqid(),-10);

5个二进制字符:

hex2bin( substr(uniqid(),-10) );

8个base64字符:

base64_encode( hex2bin( substr(uniqid(),-10) ) );