【问题标题】:Why str_shuffle always generate similar patterns?为什么 str_shuffle 总是产生相似的模式?
【发布时间】:2014-04-03 10:28:13
【问题描述】:

我正在尝试使用以下代码生成 1500 验证码:

    <?php
include "../include/top.php";

set_time_limit(0);
ini_set("memory_limit", "-1");

$end=0;
while ($end<1500)
{
//generate an authentication code
$string="ABCDEFGHJKLMNPQRSTUVWXYZ123456789";
$string= substr(str_shuffle($string),5,8) ;

//check whether generated code already exist
$query = "select count(*) from auth where code = '$string' ";
$stmt = prepare ($query);
execute($stmt);
$bind = mysqli_stmt_bind_result($stmt, $count);
check_bind_result($bind);
mysqli_stmt_fetch($stmt);
mysqli_stmt_free_result($stmt);

//If generated code does not already exist, insert it to Database table
if ($count == 0)
    {
    echo $string."<br>";
    $query = "insert into auth (Code) values ('$string')";
    $stmt = prepare ($query);
    execute($stmt);
    $end++;
    }
}
?>

它在 15 秒内生成并在数据库中插入 1024 个代码并在浏览器中打印 667 个代码,并且浏览器继续加载而不插入/打印更多代码,直到半小时后我关闭浏览器窗口。 之后,当从 WAMP 在浏览器中打开任何网页时,它显示为浏览器正在加载并且不显示内容。也就是说,在打开任何网页之前,我需要在运行此脚本后重新启动 WAMP。 这个我试过很多次了。

为什么脚本没有生成 1500 个代码并且在达到计数 667/1024 时总是停止?

更新

作为一个实验,我在 IF 条件中添加了一个 ELSE 子句,并编写了代码以在 ELSE 子句中打印“代码已存在”。并使用同一张表的空(截断)副本运行脚本,然后打印并插入 1024 个代码,然后连续打印“代码已存在”(5 分钟内约有 700 000 多个条目并继续)。同样,当使用只有 1024 行的表运行脚本时,它甚至不会打印或插入单个代码。相反,它无限地继续打印“代码已经存在”。 我观察到的另一件事是 WHILE 循环的第一个 1024 次迭代通过了 IF 条件(如果表为空)。并且所有后续迭代都失败了 IF 条件。

【问题讨论】:

  • http 服务器错误日志文件中有任何条目吗?
  • @arkascha 在 Appache 错误日志文件中未发现错误
  • @arkascha 我已经更新了我的代码。实际上我第一次发布了错误的代码。对不起..
  • 如果没有数据库结构和准备/执行函数的作用(因为它们似乎由您定义),很难进行测试,但听起来您正在泄漏 mysqli 句柄/连接。打印/存储之间的差异可能是由 httpd 中的缓冲引起的,并且由于它耗尽了可用的 MySQL 句柄,因此必须重新启动 httpd 才能让它们全部消失。
  • 代码栏有索引吗?否则,您将进行不断增长的逐行搜索以查找代码是否已存在。另外,我不会将其用作密码,因为它永远不会重复使用字符,因此您限制了最大排列数(除非您特别想这样做)。

标签: php string shuffle secret-key


【解决方案1】:

我不认为 str_shuffle 中的随机化器可以做到这一点。

如果我在获得 1024 个唯一代码后运行它,然后它只会生成重复项。如果我然后重新启动它,它将生成另外 976 个唯一代码,在数据库中总共提供 2000 个代码。

因此,我假设str_shuffle() 使用的随机发生器需要重置才能生成所需的 1500 个唯一代码。

试试这个小修改,它至少会在尝试生成唯一代码失败 15000 次后停止执行。

基本上我认为你必须想出一个更好的随机机制。

<?php
include "../include/top.php";

set_time_limit(0);
ini_set("memory_limit", "-1");

$end=0;
$dups=0;
while ($end<1500 && $dups < 15000)
{
    //generate an authentication code
    $string="ABCDEFGHJKLMNPQRSTUVWXYZ123456789";
    $string= substr(str_shuffle($string),5,8) ;

    //check whether generated code already exist
    $query = "select count(*) from auth where code = '$string' ";
    $stmt = prepare ($query);
    execute($stmt);
    $bind = mysqli_stmt_bind_result($stmt, $count);
    check_bind_result($bind);
    mysqli_stmt_fetch($stmt);
    mysqli_stmt_free_result($stmt);

    //If generated code does not already exist, insert it to Database table
    if ($count == 0) {
        echo $string."<br>";
        $query = "insert into auth (Code) values ('$string')";
        $stmt = prepare ($query);
        execute($stmt);
        $end++;
    } else {
        $dups++
        echo "DUPLICATE for $string, Dup Count = $dups<br>";
    }
}
?>

为什么你需要重启 = 你设置了 php 超时,所以它永远不会超时。

【讨论】:

  • 在发布之前没有看到您的编辑。这可能会帮助您找到更好的算法stackoverflow.com/questions/2799495/…
  • +1 也许只是为substr() 使用随机起始位置?而不是每次都使用5
【解决方案2】:

我没有看到任何具体的编码错误。使用 str_shuffle 创建验证码是很特殊的,因为它可以防止重复的字母,使可能的值范围更小。所以它可能只是在重复模式。

改用这样的方法,这样你就可以打乱打乱的字符串:

$origstring= str_shuffle("ABCDEFGHJKLMNPQRSTUVWXYZ123456789");

while ($end<1500 && $dups < 15000)
{
   $origstring = str_shuffle( $origstring);
   $string = substr( $newstring, 5, 8);

或者,使用类似这样的东西来生成字符串,这样你就可以有重复,创建更大范围的可能值:

   $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
   for ($i = 0; $i < 8; $i++)
   {
      $code .= $characters[mt_rand(0, 35)];
   }

【讨论】:

  • 第一次shuffle只执行一次,所以没有效果。另外我不想使用 FOR 循环来生成代码
  • @user2826057:但第二次洗牌在循环内并洗牌。 (虽然$newstring 应该是$origstring 什么的?)
  • @user2826057 为什么不想使用 FOR 循环来生成代码?你认为 shuffle() 在幕后做了什么?
  • @w3d 是的,newstring 应该是 origstring。如果问题是 shuffle 仅从给定字符串生成 1024 个选项,那么第二次 shuffle 会扩大该范围。
【解决方案3】:

你必须在你的php.ini配置文件中微调一些变量,找到那些:

; Maximum execution time of each script, in seconds
; http://php.net/max-execution-time
; Note: This directive is hardcoded to 0 for the CLI SAPI
max_execution_time = 600    

而且,不是强制性的,您也可以更改:

suhosin.post.max_vars = 5000   
suhosin.request.max_vars = 5000

修改后,重启你的网络服务器。

【讨论】:

  • 实际上,如果脚本由于达到某些 php 限制而被终止,那么套接字连接将被关闭:客户端将停止指示加载活动并显示到目前为止的结果或一般错误,具体取决于在到目前为止收到的标题上。相反,套接字显然保持打开状态,因此 OP 写道。
  • @bodi0 我试过你的建议,但没有结果。 PHP.INI 中没有这样的条目 suhosin.post.max_vars 和 suhosin.request.max_vars。 max_input_time 和请求数据有关,代码中没有请求数据,为什么会提到她?
  • suhosin** 变量在php.ini 配置中默认不存在,实际上max_input_time 与您的情况无关。
猜你喜欢
  • 2017-08-09
  • 2022-11-03
  • 1970-01-01
  • 2011-06-22
  • 2011-05-28
  • 2011-06-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多