2010-02-16 19 views
96

Aquí es una instantánea de mi código:¿Cómo aplicar el método bindValue en la cláusula LIMIT?

$fetchPictures = $PDO->prepare("SELECT * 
    FROM pictures 
    WHERE album = :albumId 
    ORDER BY id ASC 
    LIMIT :skip, :max"); 

$fetchPictures->bindValue(':albumId', $_GET['albumid'], PDO::PARAM_INT); 

if(isset($_GET['skip'])) { 
    $fetchPictures->bindValue(':skip', trim($_GET['skip']), PDO::PARAM_INT);  
} else { 
    $fetchPictures->bindValue(':skip', 0, PDO::PARAM_INT); 
} 

$fetchPictures->bindValue(':max', $max, PDO::PARAM_INT); 
$fetchPictures->execute() or die(print_r($fetchPictures->errorInfo())); 
$pictures = $fetchPictures->fetchAll(PDO::FETCH_ASSOC); 

consigo

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''15', 15' at line 1

Parece que DOP está añadiendo comillas simples a mi variables en la parte límite del código SQL. Lo busqué Encontré este error, que creo que está relacionado: http://bugs.php.net/bug.php?id=44639

¿Es eso lo que estoy viendo? ¡Este error se ha abierto desde abril de 2008! ¿Qué se supone que debemos hacer mientras tanto?

Necesito construir algo de paginación, y necesito asegurarme de que los datos estén limpios, sql injection-safe, antes de enviar la declaración sql.

+0

[Aquí hay una consulta relacionada pero con bindParam en su lugar] (http://stackoverflow.com/questions/15853266/pdo-bindparam-not-allowing-statement-to-return-results) –

+0

Respuesta digna de atención en una pregunta duplicada : [Consulta PDO parametrizada y cláusula \ 'LIMIT \ '- no funciona \ [duplicado \] (agosto de 2013; por Bill Karwin)] (http://stackoverflow.com/a/18006026/367456) – hakre

Respuesta

138

Recuerdo haber tenido este problema antes. Emite el valor a un entero antes de pasarlo a la función de vinculación. Creo que esto lo soluciona

$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT); 
+11

Dios, gracias. Pensé que me estaba volviendo loco. – UltimateBrent

+36

¡Gracias! Pero en PHP 5.3, el código anterior arrojó un error que decía "Error fatal: no puede pasar el parámetro 2 por referencia". No le gusta lanzar un int allí. En lugar de '(int) trim ($ _ GET ['skip'])', intente 'intval (trim ($ _ GET ['skip']))'. –

+5

sería genial si alguien proporcionara la explicación de por qué esto es así ... desde un punto de vista de diseño/seguridad (u otro). – Ross

13

Mirando el informe de error, el siguiente podría trabajar:

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT); 

$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT); 

pero ¿está seguro de que sus datos de entrada es correcta? Porque en el mensaje de error, parece haber solo una cita después del número (a diferencia del número entero entre comillas). Esto también podría ser un error con sus datos entrantes. ¿Puedes hacer un print_r($_GET); para averiguarlo?

+1

' '15', 15'. El primer número está completamente entre comillas. El segundo número no tiene citas en absoluto. Entonces sí, la información es buena. –

+0

Sí, ayudé en mi caso. ¡Gracias! – silverdr

5

para LIMIT :init, :end

que necesita vincular esa manera. si tiene algo como $req->execute(Array());, no funcionará, ya que arrojará PDO::PARAM_STR a todos los vars en la matriz y para el LIMIT absolutamente necesita un número entero. bindValue o BindParam como desee.

$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT); 
-3

hice lo siguiente en la mina donde $ información es mi arreglo que contiene mis parámetros vinculados:

preg_match('/LIMIT :(?P<limit>[0-9a-zA-Z]*)/', $sql, $matches); 
    if (count($matches)) { 
     //print_r($matches); 
     $sql = str_replace($matches[0], 'LIMIT ' . intval($info[':' . $matches['limit']]), $sql); 
    }  
    // LIMIT #, :limit# 
    preg_match('/LIMIT (?P<limit1>[0-9]*),\s?:(?P<limit2>[0-9a-zA-Z]*)/', $sql, $matches); 
    if (count($matches)) { 
     //print_r($matches); 
     $sql = str_replace($matches[0], 'LIMIT ' . $matches['limit1'] . ',' . intval($info[':' . $matches['limit2']]), $sql); 
    } 

Esto se basa en parte en el código de Sebas.

36

La solución más simple sería cambiar el emulation mode apagado. Puede hacerlo ya sea como una opción de conexión o simplemente añadiendo la siguiente línea

$PDO->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 

No sólo va a resolver su problema con el parámetro se unen, pero también le permiten enviar los valores de ejecutar(), que harán de su código espectacularmente shoter

$skip = (isset($_GET['skip'])):$_GET['skip']:0; 
$sql = "SELECT * FROM pictures WHERE album = ? ORDER BY id ASC LIMIT ?, ?"; 
$PDO->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); 
$stm = $PDO->prepare($sql); 
$stm->execute(array($_GET['albumid'],$skip,$max)); 
$pictures = $stm->fetchAll(PDO::FETCH_ASSOC); 
+0

'SQLSTATE [IM001]: El controlador no admite esta función: Este controlador no admite atributos de configuración' ... ¿Por qué nunca tan simple para * me * :) Mientras estoy seguro de que esto atraerá a la mayoría de la gente, en mi caso terminé teniendo que usar algo similar a la respuesta aceptada. ¡Solo un aviso para futuros lectores! –

+0

@MatthewJohnson ¿qué controlador es? –

+0

No estoy seguro, pero en [el manual] (http://www.php.net/manual/en/pdo.setattribute.php) dice 'PDO :: ATTR_EMULATE_PREPARES Habilita o deshabilita la emulación de las declaraciones preparadas. Algunos controladores no admiten declaraciones nativas preparadas o tienen soporte limitado para ellas. Es nuevo para mí, pero nuevamente estoy empezando con PDO. Por lo general, uso mysqli, pero pensé que trataría de ampliar mis horizontes. –

2

Esto solo como resumen.
Hay cuatro opciones para parametrizar LIMIT/OFFSET valores:

  1. Desactivar PDO::ATTR_EMULATE_PREPARES como se mencionó above.

    Lo que impide que los valores pasados ​​por ->execute([...]) aparezcan siempre como cadenas.

  2. Cambiar al manual ->bindValue(..., ..., PDO::PARAM_INT) parámetro population.

    Sin embargo, es menos conveniente que an -> ejecutar lista [].

  3. Simplemente haga una excepción aquí e interpola enteros simples al preparar la consulta SQL.

    $limit = intval($limit); 
    $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}"); 
    

    La fundición es importante. Más comúnmente ves ->prepare(sprintf("SELECT ... LIMIT %d", $num)) usado para tales propósitos.

  4. Si no está utilizando MySQL, pero por ejemplo SQLite o Postgres; también puede convertir parámetros vinculados directamente en SQL.

    SELECT * FROM tbl LIMIT (1 * :limit) 
    

    Nuevamente, MySQL/MariaDB no admite expresiones en la cláusula LIMIT. Aún no.

+1

Hubiera usado sprintf() con% d para 3, yo diría que es un poco más estable que con la variable. – hakre

+0

Sí, la interpolación de fundido varfunc no es el ejemplo más práctico. A menudo uso mi perezoso '{$ _GET-> int [" limit "]}' para tales casos. – mario

1

bindValue compensar y limitar el uso de PDO :: PARAM_INT y funcionará

1

Ya que nadie ha explicado por qué ocurre esto, estoy añadiendo una respuesta. La razón por la que se está comportando así es porque está usando trim(). Si mira el manual de PHP para trim, el tipo de devolución es string. Usted está tratando de pasar esto como PDO::PARAM_INT. Algunas formas de evitar esto son:

  1. Usa filter_var($integer, FILTER_VALIDATE_NUMBER_INT) para asegurarte de que estás pasando un número entero.
  2. Como otros dijeron, utilizando intval()
  3. casting con (int)
  4. Comprobación de si se trata de un entero con is_int()

Hay muchas más formas, pero esto es básicamente la causa raíz.

+1

Sucede incluso cuando la variable siempre ha sido un número entero. – felwithe

0

// ANTES (error presente) $ query = ".... LIMIT: p1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior);

// DESPUÉS (Error corregido) $ query = ".... LIMIT: p1, 30;"; ... $ limiteInferior = (int) $ limiteInferior; $ stmt-> bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);

Cuestiones relacionadas