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