Pregunta

Tengo una función que genera una clave de 4 caracteres que debe ser única para cada momento. Para hacer eso, la función primero genera una clave y luego verifica una tabla de la base de datos para ver si está en uso por otra persona.

Si no está en uso, devuelve la clave; de ??lo contrario, se vuelve a llamar a sí mismo, pero esto hace que la función realice un bucle infinito, que es un no-no. Aquí está toda la función:

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

¿Cuál es la forma correcta de volver a llamar a la función?

Por cierto, estoy usando CodeIgniter, por lo tanto, $ this .

¿Fue útil?

Solución

No usaría las funciones recursivas para los escenarios de reintento (ya que no reutiliza el resultado de la función, no tiene sentido usar la recursión) ... Añade mucha sobrecarga innecesaria. Haz algo como esto:

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

return $key;

Si está cerca del número máximo de claves, esto resultará en tiempos de bucle muy largos, por lo que es posible que desee establecer algún tipo de límite máximo.

Ah, y si esto ocurre en varios subprocesos simultáneamente y está verificando una base de datos, debe implementar el bloqueo de escritura en la tabla para que la misma clave no se pueda insertar dos veces. Preferiblemente, la función que verifica si una clave está disponible debería bloquear , verificar y, si está disponible, escribir en la misma transacción para evitar colisiones.

Otros consejos

Debe devolver el resultado de la llamada automática, de lo contrario, la clave válida no se devolverá una vez que se repita.

return $this->key_generator($length);
  

pero esto hace que la función realice un bucle infinito,

Si absolutamente desea mantener su estrategia recursiva, debe definir un caso final. Por ejemplo, puede definir un contador, como este:

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

Sin embargo, también es posible hacer su código de forma iterativa ...

Si incluye suficiente singularidad en su rutina de generación de claves, es posible que pueda evitar esta situación en primer lugar. P.ej. haga que la rutina tenga en cuenta la marca de tiempo actual y el nombre de host local y / o PID.

El bucle de tal manera no determinista es generalmente una prueba de que alguna parte es demasiado ingenua. Eso no es bueno. :-)


De todos modos, al menos sería una buena práctica detectarla y registrar algún tipo de error, en lugar de colgar la solicitud y finalmente agotar el tiempo de espera:

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

¿Por qué no simplemente escanear el espacio de claves valor para la primera clave no utilizada? ¿Necesita la clave para cumplir con restricciones adicionales además de tener cuatro caracteres de longitud y ser único?

Podría recordar la última clave devuelta para reanudar el escaneo desde allí en las llamadas subsiguientes.

Si desea que las llamadas subsiguientes no devuelvan claves similares, puede barajar su base de datos de claves primero. Esto significaría que debe mantener una matriz de elementos 456976, 1679616, 7311616 o 14776336 en algún lugar (dependiendo de si el alfabeto utilizado es un carácter de una o dos letras, con o sin dígitos).

Podría poner su código en un bucle y determinar la clave iterativamente en lugar de recursivamente .

Ejemplo:

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

  return $key;
}

El bucle en sí no previene un bucle de infinte, pero a diferencia de una llamada de función, esto no consume espacio de pila, por lo que no corre el riesgo de un desbordamiento de pila.

También simplifica un poco las cosas. Dependiendo del tipo de clave, también puede adaptar el método de generación de claves, por ejemplo, con las teclas numeradas puede aumentar exponencialmente con cada iteración.

Comentarios: Si es posible, use la función de incremento automático de la base de datos en lugar de rodar su propia función de generación de claves.

También asegúrese de proteger su código contra el acceso concurrente. ¿Qué pasa si dos instancias de esta función intentan generar una clave y ambas determinan lo mismo? Use secciones o transacciones críticas para asegurarse de que no pase nada malo.

Usando una función dentro de sí mismo

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top