创建随机令牌,检查是否有重复

时间:2010-09-23 20:06:20

标签: php loops for-loop

我有一个PHP脚本,可以制作随机令牌(A-Z,a-z,0-9):

    function token($length) {

    $characters = array(
    "A","B","C","D","E","F","G","H","J","K","L","M",
    "N","P","Q","R","S","T","U","V","W","X","Y","Z",
    "a","b","c","d","e","f","g","h","i","j","k","m",
    "n","o","p","q","r","s","t","u","v","w","x","y","z",
    "1","2","3","4","5","6","7","8","9");

    //make an "empty container" or array for our keys
    $keys = array();

    //first count of $keys is empty so "1", remaining count is 1-6 = total 7 times
    while(count($keys) < $length) {
        //"0" because we use this to FIND ARRAY KEYS which has a 0 value
        //"-1" because were only concerned of number of keys which is 32 not 33
        //count($characters) = 33
        $x = mt_rand(0, count($characters)-1);
        if(!in_array($x, $keys)) {
           $keys[] = $x;
        }
    }

    foreach($keys as $key){
        $random .= $characters[$key];
    }

    return $random;

}

工作完美,但我希望能够检查某个数据库,以确保以前从未使用过相同的令牌。如果有,它会在输出之前立即重新创建一个新令牌。

我知道我可以使用此脚本来检查重复:

$check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");

        if(mysqli_num_rows($check) > 0){

        //ERROR: DUPLICATE TOKEN, CREATE A NEW TOKEN NOW...

        }

我只是需要帮助才能将它们全部添加到一起,这样如果在数据库中找到重复项,它将循环返回并重试。

7 个答案:

答案 0 :(得分:6)

使用do while循环:

do {
    $random = token(LENGTH);
} while (mysqli_num_rows(mysqli_query(YOUR_LOOKUP_QUERY)));

PS:您可以简化令牌代码:

function token($length) {
    $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456789';
    return substr(str_shuffle($characters), 0, $length);
}

答案 1 :(得分:4)

将令牌创建并检查到do … while循环:

do {
    $random = token(10);
    $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");
} while (mysqli_num_rows($check) > 0);

顺便说一下:您可以按如下方式简化token功能:

function token($length) {
    $characters = array(
        "A","B","C","D","E","F","G","H","J","K","L","M",
        "N","P","Q","R","S","T","U","V","W","X","Y","Z",
        "a","b","c","d","e","f","g","h","i","j","k","m",
        "n","o","p","q","r","s","t","u","v","w","x","y","z",
        "1","2","3","4","5","6","7","8","9");
    if ($length < 0 || $length > count($characters)) return null;
    shuffle($characters);
    return implode("", array_slice($characters, 0, $length));
}

答案 2 :(得分:3)

使用令牌上的唯一键设置数据库表效率更高 - 然后尝试插入新值 - 如果查询成功,则获得新值,如果返回错误代码1022,则键已存在 - 如果它返回一个不同的错误 - 其他错误。

下进行。

答案 3 :(得分:1)

我不知道您的方案的上下文是什么,但是可能值得研究使用更加预先制作的方法来生成您的唯一令牌(桌面上的唯一ID用于实例?)

话虽如此,如果您希望对令牌的格式进行微调控制,那么可能值得研究将唯一性生成例程包装到SQL过程中,以便在一个事务中进行确定,而不是为每次迭代打开一个单独的通信请求。

这样的东西会生成1000个“独特”标记,但它可以很容易地调整为循环,直到代码真正唯一而不是固定数量的循环(并且它可能会被清理一下:)):

DECLARE @token as varchar(6),
        @numberOfLoops int,
        @currentLoop int,
        @pool varchar(50)       
-- Initialize 
SET @token = ''
SET @numberOfLoops = 1000
SET @currentLoop = 0
SET @pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890';

WHILE (@currentLoop < @numberOfLoops)
BEGIN
    SET @token = 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1) + 
        SUBSTRING(@pool, CAST((RAND() * LEN(@pool) + 1) as int), 1);
    print @token;


    SET @currentLoop = @currentLoop + 1;
END

但是,这里有一些重要的注意事项需要考虑。我能用你自己推出的令牌来考虑的最大问题是暂时的唯一性问题。根据您分配令牌的时间,它可能是唯一的w / r / t您的数据源片刻,但随后---可能 - 如果您不小心则声称下一个。

答案 4 :(得分:0)

您可以将所有代码包装在while循环中,该循环将重复代码,直到找到有效代码(不重复)。例如;

do { 
  $nextToken = token(32);
  $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$nextToken."'");
} while ( mysql_num_rows($check) > 0 );

这将重复加载随机令牌,直到找到一个不会从mysql_num_rows函数生成任何行的令牌(即之前没有重复过)。

答案 5 :(得分:0)

do-while循环对此非常合适,因为您希望在生成的令牌不唯一的情况下“生成新令牌”。

do {
    $token = token(...);
} while (token_exists($token));

正如您对token函数所做的那样,您应该将数据库查找代码放入一个新函数中,以便明确说明主算法(继续生成新令牌,直到它是唯一的)。我在上面的代码中假设该函数将被称为token_exists,看起来像这样:

function token_exists($random) {
    $check = mysqli_query($mysqli, "SELECT ".$table.".token FROM ".$table." WHERE ".$table.".token = '".$random."'");
    return (mysqli_num_rows($check) > 0);
}

答案 6 :(得分:0)

 mysql> CREATE TABLE tokens (id integer primary key auto_increment, token varchar(36), UNIQUE(token));

<?php
  mysqli_query('INSERT INTO tokens (token) SELECT UUID();');
  $r = mysqli_query('SELECT token FROM tokens WHERE id = '.mysqli_insert_id());
  $result = mysqli_fetch_row($r);
  echo $result[0];