Introduction : "Pourquoi PDO ne comprend-il pas mon tableau ?"
\nChaque développeur PHP qui travaille avec des bases de données finit tôt ou tard par devoir exécuter une requête avec l'opérateur IN(). Rien de plus simple, pense-t-on : on passe un tableau de valeurs et on récupère les données filtrées. Mais c'est là que le piège se cache.
Voici l'exemple classique qui vient à l'esprit d'un débutant :
\n<?php\n$ids = [1, 2, 3, 5];\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // Erreur !\n?>\nEt là, PDO renvoie une erreur : "Array to string conversion" ou tout simplement un résultat vide. Pourquoi ? PDO ne sait pas "déplier" les tableaux dans les requêtes SQL. Il attend une valeur scalaire pour chaque placeholder nommé, pas un tableau entier.
\nDans cet article, nous allons passer en revue les erreurs les plus fréquentes que commettent les développeurs en essayant de lier un tableau à IN(), et vous montrer les solutions correctes, sécurisées et efficaces.
Erreur n°1 : Passer naïvement un tableau dans execute()
\nC'est l'erreur la plus courante. Le développeur pense que PDO va deviner tout seul qu'il faut substituer chaque valeur du tableau dans la condition IN(). Mais comme on l'a vu, PDO ne fonctionne pas comme ça.
À quoi ça ressemble dans le code
\n<?php\n$ids = [10, 20, 30];\n$stmt = $pdo->prepare("SELECT * FROM products WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // Erreur fatale\n?>\nRésultat : Une erreur ou un avertissement concernant la conversion d'un tableau en chaîne. Même si le code « passe », PDO insérera le mot "Array" dans la requête — et vous obtiendrez WHERE id IN ('Array'), ce qui n'a aucun sens.
Pourquoi ? PDO exige qu'à chaque placeholder corresponde une seule valeur. Un tableau n'est pas une valeur, mais un ensemble de valeurs. PDO ne peut pas et ne doit pas deviner ce que vous voulez faire.
\n\nErreur n°2 : Utiliser bindValue() avec un tableau
\nParfois, les développeurs essaient de tromper PDO en utilisant bindValue() :
<?php\n$ids = [1, 2, 3];\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN (:ids)");\n$stmt->bindValue(':ids', $ids, PDO::PARAM_STR); // Erreur !\n$stmt->execute();\n?>\nMême problème : bindValue() attend une valeur scalaire. Le tableau sera converti en chaîne "Array", et la requête ne s'exécutera pas correctement. Le troisième paramètre (PDO::PARAM_STR) n'y change rien — il ne fait qu'indiquer le type de données, sans modifier la logique de gestion des tableaux.
Erreur n°3 : La concaténation directe (injection SQL)
\nDésespérés, certains développeurs prennent le chemin de la moindre résistance : ils insèrent directement le tableau dans la requête via implode() :
<?php\n$ids = [1, 2, 3];\n$ids_str = implode(',', $ids);\n$stmt = $pdo->query("SELECT * FROM users WHERE id IN ($ids_str)"); // Dangereux !\n?>\nPourquoi est-ce dangereux ? Si les valeurs du tableau proviennent de l'utilisateur (par exemple, de paramètres GET ou d'un formulaire), un attaquant peut transmettre 1; DROP TABLE users; --. La requête deviendra IN (1; DROP TABLE users; --), et votre base de données sera détruite. C'est une injection SQL classique.
Même si vous êtes sûr que les données sont sûres, cette pratique est une très mauvaise habitude. Un jour, vous oublierez de vérifier la source des données, et les conséquences seront catastrophiques.
\n\nLa bonne solution : Les placeholders dynamiques
\nPassons aux bonnes nouvelles. PDO permet de créer des placeholders de manière dynamique. Vous devez générer une chaîne avec le bon nombre de ? ou de :id1, :id2, ... et passer le tableau de valeurs séparément.
Méthode 1 : Utiliser les placeholders positionnels (?)
\nC'est la méthode la plus simple et la plus populaire :
\n<?php\n