¿Cómo aplicar el método bindValue en la cláusula LIMIT?
Pregunta
Aquí hay una instantánea de mi código:
$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);
yo obtengo
Tienes un error en tu sintaxis SQL;Verifique el manual que corresponde a su versión del servidor MySQL para que la sintaxis correcta use cerca de '' 15 ', 15' en la línea 1
Parece que PDO está agregando comillas simples a mis variables en la parte LIMIT del código SQL.Lo busqué y encontré este error que creo que está relacionado:http://bugs.php.net/bug.php?id=44639
¿Es eso lo que estoy mirando?¡Este error ha estado abierto desde abril de 2008!¿Qué se supone que debemos hacer mientras tanto?
Necesito crear algo de paginación y asegurarme de que los datos estén limpios y sean seguros para la inyección de SQL antes de enviar la declaración SQL.
Solución
recuerdo haber tenido este problema antes. Convertir el valor a un número entero antes de pasarla a la función de vinculación. Creo que esto lo resuelve.
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
Otros consejos
La solución más simple sería conmutar el modo de emulación de . 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 (), lo que hará que su código muchshorter
$skip = $_GET['skip'] ?: 0;
$sql = "SELECT * FROM pictures WHERE album = ? ORDER BY id LIMIT ?, ?";
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
$stmt = $PDO->prepare($sql);
$stmt->execute([$_GET['albumid'], $skip, $max]);
$pictures = $stmt->fetchAll(PDO::FETCH_ASSOC);
Mirando el informe de error, el siguiente podría funcionar:
$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? Debido a que en el mensaje de error, parece que hay solamente un cita después del número (en contraposición a todo el número que se está entre comillas). Esto también podría ser un error con los datos entrantes. ¿Se puede hacer un print_r($_GET);
para averiguar?
Esto es solo un resumen.
Hay cuatro opciones para parametrizar los valores LIMIT/OFFSET:
Desactivar
PDO::ATTR_EMULATE_PREPARES
como se mencionó arriba.Lo que evita que los valores pasados por
->execute([...])
para aparecer siempre como cadenas.Cambiar a manual
->bindValue(..., ..., PDO::PARAM_INT)
población de parámetros.Lo cual, sin embargo, es menos conveniente que una ->lista de ejecución[].
Simplemente haga una excepción aquí e interpola números enteros simples al preparar la consulta SQL.
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
El casting es importante.Más comúnmente ves
->prepare(sprintf("SELECT ... LIMIT %d", $num))
utilizados para tales fines.Si no está utilizando MySQL, sino 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.
para LIMIT :init, :end
Es necesario para unir de ese modo. si tenía algo así como $req->execute(Array());
que no funcionará, ya que echará PDO::PARAM_STR
a todos los VARs de la matriz y para la LIMIT
es absolutamente necesario un entero.
bindValue o BindParam como desee.
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
Ya que nadie ha explicado por qué ocurre esto, estoy añadiendo una respuesta. La razón de que se está comportando de esta era es porque está utilizando trim()
. Si nos fijamos en el manual de PHP para trim
, se string
el tipo de retorno. Luego se le tratando de pasar esto como PDO::PARAM_INT
. Algunas maneras de evitar esto son:
- Uso
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
para asegurarse de que usted está pasando un entero. - Como otros han dicho, utilizando
intval()
- La colada con un
(int)
- Comprobación de si es un número entero con
is_int()
Hay muchas más formas, pero esto es básicamente la causa raíz.
bindValue desplazamiento y limitar el uso de PDO :: PARAM_INT y funcionará
// ANTES (error presente) $ Consulta = ".... LÍMITE: p1, 30;"; ... $ Stmt-> bindParam ( 'p1', $ limiteInferior);
// DESPUÉS (error corregido) $ Consulta = ".... LÍMITE: p1, 30;"; ... $ LimiteInferior = (int) $ limiteInferior; $ Stmt-> bindParam ( 'p1', $ limiteInferior, PDO :: PARAM_INT);
PDO::ATTR_EMULATE_PREPARES
me dio la
controlador no es compatible con esta función: Este controlador no soporta Error de configuración de atributos.
Mi solución fue establecer una variable $limit
como una cadena, y luego combinarlo en la declaración preparar como en el siguiente ejemplo:
$limit = ' LIMIT ' . $from . ', ' . $max_results;
$stmt = $pdo->prepare( 'SELECT * FROM users WHERE company_id = :cid ORDER BY name ASC' . $limit . ';' );
try {
$stmt->execute( array( ':cid' => $company_id ) );
...
}
catch ( Exception $e ) {
...
}