我有一个生成4个字符的键的函数,每次都必须是唯一的。为此,该函数首先生成一个密钥,然后检查数据库表以查看其他人是否正在使用它。
如果它没有被使用,它会返回键,否则它会再次调用自身,但是这会导致函数执行无限循环,这是一个禁忌。这是整个功能:
function key_generator($length = 4)
{
// I've subsequently left out the generating code,
// which is not necesarry in this case
$key = 'xxxx';
if ($this->user_model->valid_key($key) == true)
{
return $key;
}
else
{
$this->key_generator(4);
}
}
再次调用该函数的正确方法是什么?
顺便说一句,我正在使用CodeIgniter,因此$this
。
答案 0 :(得分:25)
我不会将recursive函数用于重试场景(因为你没有重用函数的结果,使用递归是没有意义的)......它增加了很多不必要的开销。做这样的事情:
do {
$key = ...; // Generate your key here...
} while (!$this->user_model->valid_key($key));
return $key;
如果您接近最大键数,这将导致非常长的循环时间,因此您可能需要设置某种最大限制。
哦,如果同时在多个线程上发生并且您正在检查数据库,则应该实现表写锁定,以便不能插入相同的键两次。优选地,检查密钥是否可用的功能应该锁定,检查,以及在同一事务中可用写入以避免任何冲突。< / p>
答案 1 :(得分:5)
你需要返回自我调用的结果,否则一旦递归就不会返回有效的密钥。
return $this->key_generator($length);
答案 2 :(得分:3)
但这会导致函数执行无限循环,
如果你绝对想要保持递归策略,你必须定义一个结束案例。例如,您可以定义一个计数器,如下所示:
function key_generator($length = 4, $limit=5)
{
if($limit === 0) {
throw new YourException();
}
// I've subsequently left out the generating code,
// which is not necesarry in this case
$key = 'xxxx';
if ($this->user_model->valid_key($key) == true)
{
return $key;
}
else
{
return $this->key_generator(4, ($limit-1));
}
}
然而,迭代地执行代码也是可能的......
答案 3 :(得分:2)
如果在密钥生成例程中包含足够的唯一性,则可能首先可以避免这种情况。例如。让例程考虑当前时间戳和本地主机名和/或PID。
以这种不确定的方式循环通常证明某些部分过于天真。这不好。 : - )
无论如何,捕获它并记录某种错误至少是一种好习惯,而不是挂起请求并最终超时:
function key_generator($length = 4)
{
/* The $attempts_left clearly depends on how much trust
you give your key generation code combined with the key space size. */
$attempts_left = pow(16, $length) * 2;
/* ... just guessing, in case your key base is 16, i.e. [0-9a-z] for example */
do {
// ... key generation goes here ...
$key = 'xxxx';
} while ( $this->user_model->valid_key($key) == false && $attempts_left-- > 0 );
if( $attempts_left < 1 )
return false;
else
return $key;
}
答案 4 :(得分:1)
您可以将代码放入循环中,然后确定键迭代而不是 的递归强>
示例:
function key_generator($length = 4)
{
do {
$key = 'xxxx'; //TODO
if (timeOutReached()) return InvalidKey;
} while (!$this->user_model->valid_key($key))
return $key;
}
循环本身不会阻止infinte循环,但与函数调用不同,这不会占用堆栈空间,因此您不会冒着堆栈溢出的风险。
它也简化了一些事情。根据键的类型,您还可以调整键生成方法,例如使用数字键,您可以在每次迭代时以指数方式增加。
备注:如果可以,请使用数据库的自动增量功能,而不是滚动自己的密钥生成功能。
还要确保保护代码免受并发访问。如果此函数的两个实例尝试生成密钥并且它们都确定相同,该怎么办?使用关键部分或事务来确保不会发生任何不良事件。
答案 5 :(得分:1)
为什么不直接扫描第一个未使用密钥的密钥值空间?需要密钥才能在四个字符长且独特的基础上实现额外的约束?
您可以记住上次返回的密钥,以便在后续呼叫中从那里恢复扫描。
如果您希望后续调用不返回类似的键,则可以先将您的密钥数据库洗牌。这意味着您需要在某处保留456976,167916,7311616或14776336元素数组(取决于使用的字母是单字符还是双字符字符,有或没有数字)。
答案 6 :(得分:0)
在内部使用一个功能
function test($val) {
/*initialize return value by using the conditions*/
if($val>=5){
/*do something with return statement*/
return $val+10;
} else {
/*set the return default value for avoid the error throwing*/
return "default value";
}
/*return the function used for check the condition*/
return test($val);
}
echo test(4); // output "default value";
echo test(6); //output 16