Question

J'ai une fonction qui génère une clé de 4 caractères qui doit être unique à chaque fois. Pour ce faire, la fonction génère d'abord une clé, puis vérifie une table de base de données pour voir si elle est utilisée par quelqu'un d'autre.

S'il n'est pas utilisé, la clé est renvoyée, sinon, il s'appelle à nouveau, mais la fonction effectue une boucle infinie, ce qui est un non-non. Voici la fonction entière:

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);
    }
}

Quelle est la bonne façon de rappeler la fonction?

Au fait, j'utilise CodeIgniter, d'où $ this .

Était-ce utile?

La solution

Je n'utiliserais pas de fonctions récursives pour les scénarios de relance (puisque vous ne réutilisez pas le résultat de la fonction, il est inutile d'utiliser la récursion) ... Cela ajoute beaucoup de temps système inutile. Faites quelque chose comme ça:

do {
    $key = ...; // Generate your key here...
} while (!$this->user_model->valid_key($key));

return $key;

Si vous approchez le nombre maximum de clés, les temps de boucle seront très longs. Vous voudrez peut-être définir une limite maximale.

Oh, et si cela se produit simultanément sur plusieurs threads et que vous vérifiez une base de données, vous devez implémenter le verrouillage d'écriture de table afin que la même clé ne puisse pas être insérée deux fois. De préférence, la fonction qui vérifie si une clé est disponible doit verrouiller , vérifier et, le cas échéant, écrire dans la même transaction pour éviter toute collision.

Autres conseils

Vous devez renvoyer le résultat de l'appel automatique, sinon la clé valide ne sera pas renvoyée une fois qu'elle sera récursive.

return $this->key_generator($length);
  

mais cela amène la fonction à faire une boucle infinie,

Si vous voulez absolument conserver votre stratégie récursive, vous devez définir un cas final. Par exemple, vous pouvez définir un compteur, comme ceci:

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));
    }
}

Il est cependant également possible de faire votre code de manière itérative ...

Si vous incluez suffisamment d'unicité dans votre routine de génération de clé, vous pourrez peut-être éviter cette situation en premier lieu. Par exemple. demandez à la routine de prendre en compte l’horodatage actuel et le nom d’hôte local et / ou le PID.

Boucler de manière aussi non déterministe est généralement la preuve qu’une partie est trop naïve. Ce n'est pas bon. : -)

Quoi qu'il en soit, il serait au moins une bonne pratique d’attraper le problème et de consigner une sorte d’erreur plutôt que de suspendre la demande et de terminer le délai imparti:

    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;
    }

Pourquoi ne pas simplement analyser l'espace de valeurs de clé pour la première clé inutilisée? Besoin de la clé pour satisfaire les contraintes supplémentaires en plus de quatre caractères uniques et uniques?

Vous pourriez vous souvenir de la dernière touche renvoyée pour reprendre la numérisation à partir de là lors d'appels suivants.

Si vous souhaitez que les appels suivants ne renvoient pas de clés similaires, vous pouvez commencer par mélanger votre base de données de clés. Cela signifierait que vous devez conserver quelque part un tableau d'éléments 456976, 1679616, 7311616 ou 14776336 (selon que l'alphabet utilisé est un caractère simple ou à double casse, avec ou sans chiffres).

Vous pouvez mettre votre code dans une boucle et déterminer la clé de manière itérative au lieu de récursivement .

Exemple:

function key_generator($length = 4)
{
  do {
    $key = 'xxxx'; //TODO
    if (timeOutReached()) return InvalidKey;
  } while (!$this->user_model->valid_key($key))

  return $key;
}

La boucle elle-même n'empêche pas une boucle infinte, mais contrairement à un appel de fonction, elle ne consomme pas d'espace dans la pile, vous ne risquez donc pas un débordement de pile.

En outre, cela simplifie un peu les choses. En fonction du type de clé, vous pouvez également adapter la méthode de génération de clé. Par exemple, avec les clés numérotées, vous pouvez augmenter de façon exponentielle à chaque itération.

Remarques: Si cela est possible, utilisez la fonctionnalité d'incrémentation automatique d'une base de données au lieu de lancer votre propre fonctionnalité de génération de clé.

Assurez-vous également de protéger votre code contre les accès simultanés. Et si deux instances de cette fonction essayaient de générer une clé et si elles déterminaient la même chose? Utilisez des sections ou des transactions critiques pour vous assurer que rien ne se passe mal.

Utiliser une fonction à l'intérieur de lui-même

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top