2010-04-26 28 views
7

He estado utilizando PDO y estoy preparando todas mis declaraciones principalmente por razones de seguridad. Sin embargo, tengo una parte de mi código que hace ejecuta la misma declaración muchas veces con diferentes parámetros, y pensé que allí sería donde brillarían las declaraciones preparadas. Pero en realidad rompen el código ...Recursividad en declaraciones preparadas

La lógica básica del código es esta.

function someFunction($something) { 
    global $pdo; 

    $array = array(); 

    static $handle = null; 
    if (!$handle) { 
    $handle = $pdo->prepare("A STATEMENT WITH :a_param"); 
    } 

    $handle->bindValue(":a_param", $something); 
    if ($handle->execute()) { 
    while ($row = $handle->fetch()) { 
     $array[] = someFunction($row['blah']); 
    } 
    } 

    return $array; 
} 

Me pareció bien, pero le faltaban muchas filas. Eventualmente me di cuenta de que el asa de la declaración estaba siendo modificada (ejecutada con diferentes param), lo que significa que la llamada a buscar en el ciclo while solo funcionará una vez, luego la función se llama de nuevo y el conjunto de resultados cambia.

Así que me pregunto cuál es la mejor forma de usar declaraciones preparadas con PDO de forma recursiva.

Una forma podría ser usar fetchAll(), pero dice en el manual que tiene una sobrecarga considerable. El objetivo de esto es hacerlo más eficiente.

La otra cosa que podría hacer es no reutilizar un identificador estático, y en su lugar crear uno nuevo cada vez. Creo que dado que la cadena de consulta es la misma, internamente el controlador de MySQL usará una declaración preparada de todos modos, por lo que solo existe la pequeña sobrecarga de crear un nuevo identificador en cada llamada recursiva. Personalmente creo que eso derrota el punto.

¿O hay alguna forma de volver a escribir esto?

+0

que acaba de hacer alguna (no supercientífico) probando mi código de función recursiva muy similar y tengo conc Creo que, para mi propósito, las declaraciones preparadas son más lentas en general. ¿De qué sirve preparar una declaración si debe volver a prepararla antes de cada uso? Debo estar perdiendo algo. –

Respuesta

2

No puede anidar los identificadores de las instrucciones: debe cerrar el descriptor de contexto previamente abierto antes de abrir otro en una sola sesión.

De hecho, PDO lo hace automáticamente cuando emite un nuevo prepararse.

Cuando se llama a la función recursiva:

  • El mango inicial se asigna (1)
  • El primer registro se trae de (1)
  • La función se llama de forma recursiva. El valor de (1) reside en la pila de recursión.
  • El nuevo mango se asigna (2), invalidar (1)
  • El primer registro se trae de (2)
  • La función devuelve
  • Intenta buscar el siguiente registro de (1) y fallar ya que es inválida

Dicho esto, MySQL no es compatible con la recursividad en su lado y eso significa que tendrá que hacerlo en el lado PHP, usando fetchAll.

+0

¿A qué manija te refieres? Puede tener muchos identificadores de declaraciones preparados en una sola sesión (creo que hay un límite de aproximadamente 500 declaraciones preparadas por servidor por sesión y declaraciones 'max_prepared_stmt_count' en total, pero esto no se aplica a las declaraciones preparadas creadas utilizando la biblioteca del lado del cliente) Los resultados no tienen su propio manejo. – outis

0

El problema real es que $handle es estático. Las variables estáticas son problemáticas para la recursión cuando el estado necesita preservarse a través de una llamada recursiva, no solo para las declaraciones preparadas. En este caso, la llamada recursiva ejecuta una nueva consulta, descartando el estado anterior. PDO::fetchAll es de hecho la única opción si desea una única consulta preparada.

Dependiendo de la afirmación, podría reescribirla para devolver todos los resultados a la vez, construyendo el árbol después de eso.

+0

@outis: '$ handle' es estático por una razón: solo puede haber una declaración activa dentro de una sesión. – Quassnoi

+0

@Quassnoi: me doy cuenta de que hay una razón para que '$ handle' sea estático. Sin embargo, las variables estáticas simplemente no funcionarán de la manera que Rob las quiere en las llamadas recursivas. – outis

+0

@Quassnoi: ¿y qué quiere decir con "activo"? Puede haber muchas declaraciones preparadas en una sola sesión, aunque cada una solo contendrá un único conjunto de resultados. – outis

0

Si usa las mismas variables, (debido a pdo bindValue) cada valor de tiempo es el mismo que el primero. Así que esto va a fracasar:

foreach ($bind_params as $key => $value) { 
    $stmt->bindParam(":$key", $value); 
} 

resultado:

$key[0] = $value[0]; 
$key[1] = $value[0]; 
$key[2] = $value[0]; 
$key[3] = $value[0]; 

por lo que quieren hacer feo truco, entonces:

 $i = 0; 
     foreach ($bind_params as $key => $value) { 
      $i++; 
      $$i = $value; 
      $stmt->bindParam(":$key", $$i); 
     } 

resultado:

$key[0] = $value[0]; 
$key[1] = $value[1]; 
$key[2] = $value[2]; 
$key[3] = $value[3]; 
Cuestiones relacionadas