如何在自己内部调用函数?

时间:2009-10-23 08:58:17

标签: php function codeigniter recursion

我有一个生成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

7 个答案:

答案 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