2010-02-16 5 views
96

Voici un aperçu de mon code:Comment appliquer la méthode bindValue dans la clause 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); 

Je reçois

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

Il semble que PDO ajoute des guillemets simples à mes variables dans la partie limite du code SQL. J'ai regardé vers le haut j'ai trouvé ce bogue que je pense est lié: http://bugs.php.net/bug.php?id=44639

Est-ce que c'est ce que je regarde? Ce bug a été ouvert depuis avril 2008! Que sommes-nous censés faire en attendant?

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

+0

[Voici une quité connexe mais avec bindParam à la place] (http://stackoverflow.com/questions/15853266/pdo-bindparam-not-allowing-statement-to-return-results) –

+0

Réponse remarquable dans une question en double : [Requête PDO paramétrée et clause \ "LIMIT \' - ne fonctionne pas \ [doublon \] (août 2013; par Bill Karwin)] (http://stackoverflow.com/a/18006026/367456) – hakre

Répondre

138

Convertissez la valeur en entier avant de la transmettre à la fonction de liaison. Je pense que cela résout.

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

Dieu, merci. Je pensais que je devenais fou. – UltimateBrent

+36

Merci! Mais en PHP 5.3, le code ci-dessus a jeté une erreur disant "Erreur fatale: Impossible de transmettre le paramètre 2 par référence". Il n'aime pas lancer un int là-bas. Au lieu de '(int) trim ($ _ GET ['skip'])', essayez 'intval (trim ($ _ GET ['skip']))'. –

+5

serait cool si quelqu'un a fourni l'explication pourquoi c'est tellement ... d'un point de vue conception/sécurité (ou autre). – Ross

13

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 seulement une citation après le nombre (par opposition au nombre entier étant placé entre guillemets). Cela peut également être une erreur avec vos données entrantes. Pouvez-vous faire un print_r($_GET); pour le savoir? Je me souviens d'avoir eu ce problème auparavant.

+1

'' 15 ', 15 '. Le premier nombre est entièrement entouré de guillemets. Le deuxième nombre n'a pas de guillemets. Alors oui, les données sont bonnes. –

+0

Yup - aidé dans mon cas. Merci! – silverdr

5

pour LIMIT :init, :end

Vous devez lier cette façon. si vous aviez quelque chose comme $req->execute(Array()); cela ne fonctionnera pas car il va lancer PDO::PARAM_STR à toutes les vars dans le tableau et pour le LIMIT vous avez absolument besoin d'un nombre entier. BindValue ou BindParam comme vous le souhaitez.

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

j'ai fait ce qui suit sur le mien où $ info est mon tableau contenant mes paramètres liés:

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); 
    } 

Ceci est basé en partie sur le code de Sebas.

36

La solution la plus simple serait d'éteindre le emulation mode. 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 shoter de façon spectaculaire

$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]: Le pilote ne supporte pas cette fonction: Ce pilote ne supporte pas les attributs de réglage ... Pourquoi n'est-il jamais si simple pour * moi * :) Alors que je suis sûr que cela va attirer la plupart des gens là-bas, dans mon cas, j'ai fini par devoir utiliser quelque chose de similaire à la réponse acceptée. Juste une tête aux futurs lecteurs! –

+0

@MatthewJohnson quel pilote c'est? –

+0

Je ne suis pas sûr, mais dans [le manuel] (http://www.php.net/manual/en/pdo.setattribute.php) il est dit 'PDO :: ATTR_EMULATE_PREPARES Active ou désactive l'émulation des instructions préparées. Certains pilotes ne prennent pas en charge les instructions préparées natives ou disposent d'un support limité ». C'est nouveau pour moi, mais là encore, je commence juste avec PDO. D'habitude, j'utilise mysqli, mais je pense que j'essaierais d'élargir mes horizons. –

2

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

  1. Désactiver PDO::ATTR_EMULATE_PREPARES comme mentionné above.

    Ce qui empêche les valeurs transmises par ->execute([...]) de toujours apparaître comme des chaînes.

  2. Passez à la population de paramètres ->bindValue(..., ..., PDO::PARAM_INT).

    Ce qui est cependant moins pratique qu'une -> liste d'exécution []. Il suffit de faire une exception ici et d'interpoler des entiers simples lors de la préparation de la requête SQL.

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

    La coulée est importante. Plus communément, vous voyez ->prepare(sprintf("SELECT ... LIMIT %d", $num)) utilisé à de telles fins.

  3. Si vous n'utilisez pas MySQL, mais par exemple SQLite, ou Postgres; vous pouvez également convertir des paramètres liés directement dans 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.

+1

J'aurais utilisé sprintf() avec% d pour 3, je dirais que c'est un peu plus stable qu'avec la variable. – hakre

+0

Ouais, le casting varfunc + interpolation n'est pas l'exemple le plus pratique. J'utiliserais souvent mon paresseux '{$ _GET-> int [" limite "]}' pour de tels cas. – mario

1

bindValue offset et limite en utilisant PDO :: PARAM_INT et il travaillera

1

Comme personne n'a expliqué pourquoi cela se produit, j'ajoute une réponse. La raison pour laquelle il se comporte c'est parce que vous utilisez trim(). Si vous regardez le manuel de PHP pour trim, le type de retour est string. Vous essayez ensuite de passer cela comme PDO::PARAM_INT. Voici quelques moyens de contourner ce problème:

  1. Utilisez filter_var($integer, FILTER_VALIDATE_NUMBER_INT) pour vous assurer que vous transmettez un nombre entier.
  2. Comme d'autres ont dit, en utilisant intval()
  3. CASTING (int)
  4. Vérification si elle est un entier avec is_int()

Il y a beaucoup d'autres façons, mais cela est essentiellement la cause racine.

+1

Cela arrive même si la variable a toujours été un entier. – felwithe

0

// AVANT (erreur actuelle) $ query = ".... LIMIT: p1, 30;"; ... $ stmt-> bindParam (': p1', $ limiteInferior); // APRÈS (Erreur corrigée) $ query = ".... LIMIT: p1, 30;"; ... $ limiteInferior = (int) $ limiteInférieur; $ stmt-> bindParam (': p1', $ limiteInferior, PDO :: PARAM_INT);

Questions connexes