如何在PHP中创建一个有效的编码/解码唯一ID


How to create a efficient encode/decode unique ID in PHP

我正试图找到一种方法将数据库ID编码为短URL,例如1应该成为"Ys47R"。然后我想把它从"Ys47R"解码回1,这样我就可以使用INT值运行数据库搜索。需要使用数据库ID唯一。序列不应该容易猜测,如1 = "Ys47R", 2 = "Ys47S"。它应该是YouTube或bitly的URL's之类的东西。我已经阅读了数百种不同的来源,使用md5, base32, base64和' bcpow,但都是空的。

这篇博客文章看起来很有希望,但是一旦我添加了填充和密码,短ID如1变成了SDDDG, 2变成了"SDDDH",3变成了"SDDDI"。它不是随机的。

base32仅使用a-b 0-9base64的末尾有==等字符

然后我试了这个:

function getRandomString($db, $length = 7) {
    $validCharacters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $validCharNumber = strlen($validCharacters);
    $result = "";
    for ($i = 0; $i < $length; $i++) {
        $index = mt_rand(0, $validCharNumber - 1);
        $result .= $validCharacters[$index];
    }

这是有效的,但意味着我必须每次运行一个数据库查询,以确保没有冲突,它不存在于数据库中。

是否有一种方法,我可以创建简短的ID's,这是4个字符最少的[a-z][A-Z][0-9]的字符集,可以编码和解码回来,使用增量唯一的ID在数据库中,每个数字都是唯一的。我无法理解使用base32base64的高级技术。

还是我研究得太多了,有更简单的方法?是否最好执行上面的随机字符串函数并查询数据库以始终检查唯一性?

您可以使用function from comments: http://php.net/manual/en/function.base-convert.php#106546

$initial = '11111111';
$dic = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
var_dump($converted = convBase($initial, '0123456789', $dic)); 
// string(4) "KCvt"
var_dump(convBase($converted, $dic, '0123456789')); 
// string(8) "11111111"
function convBase($numberInput, $fromBaseInput, $toBaseInput)
{
    if ($fromBaseInput==$toBaseInput) return $numberInput;
    $fromBase = str_split($fromBaseInput,1);
    $toBase = str_split($toBaseInput,1);
    $number = str_split($numberInput,1);
    $fromLen=strlen($fromBaseInput);
    $toLen=strlen($toBaseInput);
    $numberLen=strlen($numberInput);
    $retval='';
    if ($toBaseInput == '0123456789')
    {
        $retval=0;
        for ($i = 1;$i <= $numberLen; $i++)
            $retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i)));
        return $retval;
    }
    if ($fromBaseInput != '0123456789')
        $base10=convBase($numberInput, $fromBaseInput, '0123456789');
    else
        $base10 = $numberInput;
    if ($base10<strlen($toBaseInput))
        return $toBase[$base10];
    while($base10 != '0')
    {
        $retval = $toBase[bcmod($base10,$toLen)].$retval;
        $base10 = bcdiv($base10,$toLen,0);
    }
    return $retval;
}

如果你想要一些对称混淆,那么base_convert()通常是足够的。

base_convert($id, 10, 36);

将返回像1i0g这样的字符串并将它们转换回来。

在基础转换之前和之后,可以添加:

  • 要获得最小字符串长度,我建议将70000添加到$id。在接收端,只需再减去它。

  • 一个较小的乘法$id *= 3将在生成的字母数字ID范围中添加一些"洞",但不会耗尽可用的字符串空间。

  • 对于一些任意的外观,一点点移动:

    $id = ($id & 0xF0F0F0F) << 4    
        | ($id & 0x0F0F0F0) >> 4;
    

    用于生成混淆的ID字符串,并返回原始的ID字符串。

    要非常清楚:这是没有任何类型的加密。它只是在连续的数字之间移动数字跳跃,看起来更随意。

您可能仍然不喜欢答案,但是在数据库中生成随机ID是真正阻止ID猜测的唯一方法。