php 中的短唯一 ID
-
08-07-2019 - |
题
我想创建一个唯一的 ID,但是 uniqid()
正在给予类似的东西 '492607b0ee414'
. 。我想要的是类似于tinyurl给出的东西: '64k8ra'
. 。越短越好。唯一的要求是它不应该有明显的顺序,并且它应该看起来比看似随机的数字序列更漂亮。字母优先于数字,并且最好不要混合大小写。由于条目数量不会那么多(最多 10000 个左右),因此碰撞风险并不是一个大因素。
任何建议表示赞赏。
解决方案
创建一个小函数,返回给定长度的随机字母:
<?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包含<!> quot; + <!> quot;或<!> quot; / <!> quot;。 要解决这个问题,你必须明确地制作模式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);
}
我花了很长时间才发现,也许这让别人感到头疼
您可以用更少的代码来实现这一点:
function gen_uid($l=10){
return substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyz"), 0, $l);
}
结果(示例):
- cjnp56brdy
- 9d5uv84zfa
- ih162lryez
- ri4ocf6tkj
- xj04s83egi
有两种方法可以获得可靠的唯一ID:使其变得如此长且可变,以至于碰撞的可能性非常小(与GUID一样)或将所有生成的ID存储在表中以供查找(在内存中或在数据库或文件中)以验证生成时的唯一性。
如果您真的问如何生成这样一个短密钥并保证其独特性而不进行某种重复检查,答案是,您不能。
这是我用于任意长度的随机base62s的例程...
调用gen_uuid()
返回WJX0u0jV, E9EMaZ3P
等<。p>之类的字符串
默认情况下,它返回8位数,因此空间为64 ^ 8或大约10 ^ 14, 这通常足以使碰撞非常罕见。
对于更大或更小的字符串,根据需要传入$ len。没有限制长度,因为我追加到[满足128个字符的安全限制,可以删除]。
注意,在md5中使用随机盐 [如果您愿意,可以使用sha1],因此不能轻易进行逆向工程。
我在网上找不到任何可靠的base62转换,因此这种方法从base64结果中删除了chars。
在BSD许可下自由使用, 享受,
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);
}
非常简单的解决方案:
使用以下内容制作唯一ID:
$id = 100;
base_convert($id, 10, 36);
再次获取原始值:
intval($str,36);
不能因为它来自另一个堆栈溢出页面而受到赞誉,但我认为解决方案非常优雅且非常棒,以至于值得复制到此线程以供引用此功能的人使用。
如果要来回转换,可以使用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示例来解决它。不要让这种默默无闻取代安全。
我想出了一个非常酷的解决方案,没有进行唯一性检查。我以为我会为未来的访客分享。
计数器是保证唯一性的一种非常简单的方法,或者如果您使用数据库,主键也可以保证唯一性。问题是它看起来很糟糕,而且可能很脆弱。所以我接受了序列并用密码混淆了它。由于密码可以反转,我知道每个id都是唯一的,但仍然是随机出现的。
这是python而不是php,但我在这里上传了代码: https://github.com/adecker89/Tiny-Unique-Identifiers
字母很漂亮,数字很难看。 你想要随机字符串,但不要<!>“丑陋的<!>”;随机字符串?
创建一个随机数并以 alpha-style ( base-26 )打印,就像预订<!> quot; numbers <!> quot;航空公司给予的。
据我所知,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');
}
你也可以这样做:
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;
}
你可以这样做 没有 以干净且易于阅读的方式处理不干净/昂贵的内容,例如循环、字符串连接或对 rand() 的多次调用。另外,最好使用 mt_rand()
:
function createRandomString($length)
{
$random = mt_rand(0, (1 << ($length << 2)) - 1);
return dechex($random);
}
如果您需要字符串在任何情况下都具有精确的长度,只需用零填充十六进制数字:
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 的十六进制数表示方式是有限的。这将是
$length <= 8
至少 在 32 位系统上,PHP 对此的限制应该是 4.294.967.295 。 - PHP 的随机数生成器也有一个最大值。为了
mt_rand()
至少 在 32 位系统上,它应该是 2.147.483.647 - 因此理论上您的 ID 数限制为 2.147.483.647。
回到主题——直观 do { (generate ID) } while { (id is not uniqe) } (insert id)
有一个缺点和一个可能的缺陷,可能会让你直接走向黑暗......
退税: 验证结果是悲观的。像这样做 总是 需要检查数据库。拥有足够的密钥空间(例如,10k 条目的长度为 5)不太可能经常导致冲突,尽管可能如此 比较地 仅尝试存储数据并仅在发生 UNIQUE KEY 错误时重试,从而消耗更少的资源。
缺陷: 用户A 检索经验证尚未使用的 ID。然后代码将尝试插入数据。但与此同时, 用户B 进入相同的循环,不幸的是检索到相同的随机数,因为 用户A 尚未存储,并且该 ID 仍然是免费的。现在系统存储 用户B 或者 用户A, ,并且当尝试存储第二个用户时,同时已经存在另一个用户 - 具有相同的 ID。
无论如何,您都需要处理该异常,并且需要使用新创建的 ID 重新尝试插入。在保持悲观检查循环(您需要重新输入)的同时添加此内容将导致代码非常丑陋且难以遵循。 幸运的是,解决这个问题的方法与解决缺点的方法是一样的: 首先尝试存储数据。如果出现 UNIQUE KEY 错误,只需使用新 ID 重试。
看看这篇文章
它解释了如何从 bdd id 生成简短的唯一 id,就像 youtube 那样。
实际上,文章中的功能与 PHP函数base_convert 它将一个数字从一个基数转换为另一个基数(但最多只能到 36 基数)。
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()));
最佳答案:最小的唯一<!>“Hash Like <!>”;给定唯一数据库ID的字符串 - PHP解决方案,无需第三方库。
以下是代码:
<?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;
}
?>