web-dev-qa-db-fra.com

Comment appliquer la méthode bindValue dans la clause LIMIT?

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; Consultez le manuel correspondant à la version de votre serveur MySQL pour le syntaxe correcte à utiliser près de '' 15 ', 15' à ligne 1

Il semble que PDO ajoute des guillemets simples à mes variables dans la partie LIMIT du code SQL. J'ai cherché ce bug que je pense lié: http://bugs.php.net/bug.php?id=44639

Est-ce ce que je regarde? Ce bogue est ouvert depuis avril 2008! Que sommes-nous censés faire entre-temps?

J'ai besoin de construire une certaine pagination, et de m'assurer que les données sont propres, sans danger pour l'injection SQL, avant d'envoyer l'instruction SQL.

106
Nathan H

Je me souviens d'avoir eu ce problème avant. Convertit la valeur en entier avant de la transmettre à la fonction bind. Je pense que cela résout le problème.

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

La solution la plus simple serait de changer le mode d'émulation off. Vous pouvez le faire soit comme une option de connexion, soit simplement en ajoutant la ligne suivante.

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

Cela ne résoudra pas seulement votre problème avec bind param, mais vous permettra également d’envoyer des valeurs dans execute (), ce qui rendra votre code éclatant

$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);
39
Your Common Sense

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 entrantes sont correctes? Parce que dans le message d'erreur, il semble n'y avoir que un citation après le nombre (par opposition au nombre entier entre guillemets) Cela pourrait aussi être une erreur avec vos données entrantes. Pouvez-vous faire un print_r($_GET); pour le savoir?

15
Pekka 웃

pour LIMIT :init, :end

Vous devez vous lier de cette façon. si vous aviez quelque chose comme $req->execute(Array());, cela ne fonctionnerait pas, car cela transverrait PDO::PARAM_STR sur tous les vars du tableau et pour LIMIT vous avez absolument besoin d'un Integer . bindValue ou de BindParam selon vos souhaits.

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

Ceci juste comme résumé.
Il existe quatre options pour paramétrer les valeurs LIMIT/OFFSET:

  1. Désactiver PDO::ATTR_EMULATE_PREPARES comme mentionné ci-dessus .

    Ce qui empêche les valeurs passées par ->execute([...]) de toujours s'afficher sous forme de chaînes.

  2. Basculer sur le paramètre ->bindValue(..., ..., PDO::PARAM_INT) manuel.

    Ce qui est cependant moins pratique qu'une liste -> execute [].

  3. Faites simplement une exception ici et interpolez simplement des entiers 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. Le plus souvent, vous voyez ->prepare(sprintf("SELECT ... LIMIT %d", $num)) utilisé à ces fins.

  4. Si vous n'utilisez pas MySQL, mais par exemple SQLite ou Postgres; vous pouvez également transtyper les paramètres liés directement en SQL.

     SELECT * FROM tbl LIMIT (1 * :limit)
    

    Encore une fois, MySQL/MariaDB ne supporte pas les expressions de la clause LIMIT. Pas encore.

6
mario

Puisque personne n’a expliqué pourquoi cela se produit, j’ajoute une réponse. La raison en est que vous utilisez trim(). Si vous consultez le manuel PHP pour trim, le type de retour est string. Vous essayez ensuite de passer ceci en tant que PDO::PARAM_INT. Voici quelques façons de contourner ce problème:

  1. Utilisez filter_var($integer, FILTER_VALIDATE_NUMBER_INT) pour vous assurer de passer un entier.
  2. Comme d'autres l'ont dit, en utilisant intval()
  3. Casting avec (int)
  4. Vérifier s'il s'agit d'un entier avec is_int()

Il y a beaucoup plus de moyens, mais c'est fondamentalement la cause première.

2
Melissa Williams

bindValue offset et limit en utilisant PDO :: PARAM_INT et cela fonctionnera

1
Karel

PDO::ATTR_EMULATE_PREPARES m'a donné le 

Le pilote ne supporte pas cette fonction: Ce pilote ne supporte pas paramétrage d'erreur des attributs.

Ma solution de contournement consistait à définir une variable $limit en tant que chaîne, puis à la combiner dans l'instruction de préparation 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 ) {
    ...
}
0
Fins

// BEFORE (Erreur actuelle) $ Query = ".... LIMIT: p1, 30;"; ... $ Stmt-> bindParam (': p1', $ limiteInferior);

// AFTER (erreur corrigée) $ Query = ".... LIMIT: p1, 30;"; ... $ LimiteInferior = (int) $ limiteInferior; $ Stmt- > bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);