在PHP中简短的唯一ID

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

任何build议感激。

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

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

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

花了我一段时间才发现,也许这是救人头痛的问题

你可以用较less的代码来实现这一点:

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

结果(示例):

  • cjnp56brdy
  • 9d5uv84zfa
  • ih162lryez
  • ri4ocf6tkj
  • xj04s83egi

有两种方法可以获得一个可靠的唯一ID:使它变得如此漫长和变化,碰撞的机会非常小(如GUID),或者将所有生成的ID存储在查找表中(无论是在内存中还是在数据库或一个文件)来validation一代的唯一性。

如果你真的在问如何生成这样一个简短的密钥,并保证其独特性没有重复检查,答案是,你不能。

真正简单的解决scheme:

使用以下唯一ID:

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

再次获得原始值:

 intval($str,36); 

因为它来自另一个堆栈溢出页面,所以不能称赞这一点,但我认为解决scheme是如此的优雅和真棒,值得复制到这个线程的人参考这个。

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

调用gen_uuid()返回像WJX0u0jV, E9EMaZ3P等string

默认情况下,这返回8位数字,因此64 ^ 8或大约10 ^ 14的空间,这往往足以使碰撞相当罕见。

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

注意,在md5中使用一个随机盐[或者如果你喜欢sha1],所以它不能很容易地被反向devise。

我没有在网上find任何可靠的base62转换,因此这种从base64结果剥离字符的方法。

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

胃食道逆stream

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

我想出了我认为是一个非常酷的解决scheme做这个没有唯一性检查。 我想我会分享任何未来的游客。

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

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

你可以使用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 

聪明的人也许可以用足够的例子来解决这个问题。 不要让这个模糊取代安全。

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

创build一个随机数字并以alpha风格(base-26)打印,就像航空公司给出的预订“号码”一样。

就我所知,PHP中没有通用的基础转换函数,因此您需要自己编写这些代码。

另一种select:使用uniqid()并摆脱数字。

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

或者用字母replace它们:

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

你也可以像tihs那样做:

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

你可以做到这一点, 没有不干净/昂贵的东西,如循环,string连接或多次调用rand(),在一个干净和容易阅读的方式。 另外,最好使用mt_rand()

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

如果你在任何情况下都需要string来确定长度,只需用hex填充hex数:

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

“理论上的倒退”是,你被限制在PHP的能力上 – 但在这种情况下,这更像是一个哲学问题;)不pipe怎么说,

  • PHP在它可以表示为一个hex数字的时候是有限的。 这至less在32位系统上是$length <= 8 ,PHP的限制应该是4.294.967.295。
  • PHP的随机数发生器也有一个最大值。 对于至less在32位系统上的mt_rand() ,应该是2.147.483.647
  • 所以你在理论上限制在2.147.483.647的ID。

回到主题 – do { (generate ID) } while { (id is not uniqe) } (insert id)直观的do { (generate ID) } while { (id is not uniqe) } (insert id)有一个缺点和一个可能的缺陷,可能会驱使你直接到黑暗…

缺点:validation是悲观的。 这样做总是需要在数据库检查。 拥有足够的密钥空间(例如,10k条目的长度为5)很可能不会像往常一样导致冲突,因为只需要尝试存储数据并仅在出现唯一密钥错误的情况下重试,可能会比较节省资源。

缺陷: 用户A检索一个被validation为未被采用的ID。 然后代码将尝试插入数据。 但与此同时, 用户B进入相同的循环,不幸的是检索相同的随机数,因为用户A还没有存储,这个ID仍然是免费的。 现在系统存储用户B用户A ,当试图存储第二个用户时,已经存在另一个用户B – 具有相同的ID。

无论如何,您都需要处理该exception,并需要重新尝试使用新创build的ID进行插入。 在保持悲观检查循环(你需要重新input)的情况下添加这个会导致相当难看和难以遵循的代码。 幸运的是,解决这个问题的方法与缺点是一样的:首先去做,然后尝试存储数据。 如果发生UNIQUE KEY错误,只需重试一个新的ID。

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

如果你喜欢更长版本的唯一ID使用这个:
$ uniqueid = sha1(md5(time()));

看看这篇文章

  • 用PHP创build简短的ID–比如Youtube或者TinyURL

它解释了如何从您的bdd ID生成简短的唯一标识符,例如youtube。

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

最佳答案然而: 最小的唯一“哈希像”string给予唯一的数据库ID – PHP解决scheme,没有第三方库的要求。

代码如下:

 <?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; } ?> 
Interesting Posts