Comment appliquer la méthode bindValue à la clause LIMIT?
Question
Voici un aperçu de mon code:
$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);
Je reçois
Vous avez une erreur dans votre syntaxe SQL; vérifier le manuel qui correspond à votre version du serveur MySQL pour la bonne syntaxe à utiliser près de « » 15' , 15' à ligne 1
Il semble que PDO ajoute des guillemets simples à mes variables dans la partie limite du code SQL. Je l'ai cherché, j'ai trouvé ce bug que je pense est lié: http://bugs.php.net/bug.php?id=44639
Est-ce que je regarde? Ce bogue a été ouvert depuis Avril 2008! Que sommes-nous censés faire en attendant?
Je dois construire une mise en page, et devez vous assurer que les données sont propres, l'injection de sécurité sql, avant d'envoyer l'instruction SQL.
La solution
Je me souviens d'avoir ce problème avant. Monter la valeur à un nombre entier avant de le transmettre à la fonction de liaison. Je pense que cela résout.
$fetchPictures->bindValue(':skip', (int) trim($_GET['skip']), PDO::PARAM_INT);
Autres conseils
La solution la plus simple serait de changer le mode d'émulation de . Vous pouvez le faire soit comme une option de connexion ou simplement en ajoutant la ligne suivante
$PDO->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
Il ne sera pas seulement de résoudre votre problème avec le paramètre de liaison, mais aussi vous laisser d'envoyer des valeurs dans l'exécution (), qui fera votre code 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);
En regardant le rapport de bogue, ce qui suit pourrait fonctionner:
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
$fetchPictures->bindValue(':skip', (int)trim($_GET['skip']), PDO::PARAM_INT);
mais êtes-vous sûr que vos données d'entrée est correcte? Parce que dans le message d'erreur, il semble y avoir que un citation après le numéro (par opposition au nombre entier étant entre guillemets). Cela pourrait aussi être une erreur avec vos données entrantes. Pouvez-vous faire un print_r($_GET);
pour savoir?
comme résumé.
Il y a quatre options pour paramétrer les valeurs limites / OFFSET:
-
Disable
PDO::ATTR_EMULATE_PREPARES
comme mentionné ci-dessus .Ce qui empêche les valeurs transmises par
->execute([...])
pour montrer toujours sous forme de chaînes. -
Passez à la population des paramètres manuel
->bindValue(..., ..., PDO::PARAM_INT)
.Ce qui est cependant moins pratique qu'un -> exécuter la liste []
.
-
Il suffit de faire une exception ici et juste interpoler entiers simples lors de la préparation de la requête SQL.
$limit = intval($limit); $s = $pdo->prepare("SELECT * FROM tbl LIMIT {$limit}");
Le casting est important. Plus souvent que vous voyez
->prepare(sprintf("SELECT ... LIMIT %d", $num))
utilisé à de telles fins. -
Si vous ne l'utilisez MySQL, mais par exemple SQLite, ou Postgres; vous pouvez également jeter directement dans les paramètres liés SQL.
SELECT * FROM tbl LIMIT (1 * :limit)
Encore une fois, MySQL / MariaDB ne prennent pas en charge les expressions dans la clause LIMIT. Pas encore.
pour LIMIT :init, :end
Vous devez lier cette façon. si vous aviez quelque chose comme $req->execute(Array());
il ne fonctionnera pas comme il jettera PDO::PARAM_STR
à tous vars dans le tableau et le LIMIT
vous devez absolument un entier.
bindValue ou BindParam que vous voulez.
$fetchPictures->bindValue(':albumId', (int)$_GET['albumid'], PDO::PARAM_INT);
Comme personne n'a expliqué pourquoi cela se produit, j'ajoute une réponse. La raison pour laquelle il se comporte c'était parce que vous utilisez trim()
. Si vous regardez le manuel PHP pour trim
, le type de retour est string
. Vous êtes alors essayez de passer cela comme PDO::PARAM_INT
. Quelques façons de contourner ce sont:
- Utilisez
filter_var($integer, FILTER_VALIDATE_NUMBER_INT)
pour vous assurer que vous passez un entier. - Comme d'autres ont dit, en utilisant
intval()
- Castings avec
(int)
- Vérification si elle est un nombre entier avec
is_int()
Il y a beaucoup d'autres façons, mais cela est essentiellement la cause racine.
bindValue décalage et limite en utilisant PDO :: PARAM_INT et il fonctionnera
// AVANT (erreur actuelle) $ query = ".... LIMIT: p1, 30;"; ... $ Stmt-> bindParam ( ': p1', limiteInferior $);
// APRES (correction d'erreur) $ query = ".... LIMIT: p1, 30;"; ... $ LimiteInferior = (int) limiteInferior $; $ Stmt-> bindParam ( ': p1', limiteInferior $, PDO :: PARAM_INT);
PDO::ATTR_EMULATE_PREPARES
m'a donné le
Le pilote ne prend pas en charge cette fonction: Ce pilote ne prend pas en charge l'erreur de réglage des attributs.
Ma solution a été de définir une variable de $limit
comme une chaîne, puis le combiner dans la déclaration préparer comme dans l'exemple suivant:
$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 ) {
...
}