IN() dans PDO : comment passer un tableau sans douleur ni erreurs

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

« Puis-je lier un tableau à une condition IN() ? » — une question qui tourmente un développeur PHP sur deux

\n\n

Avez-vous déjà écrit une requête comme SELECT * FROM users WHERE id IN (?) et essayé d’y passer un tableau via bindValue() ? Si oui, vous connaissez sûrement ce sentiment de frustration lorsque PDO ne retourne qu’une seule ligne au lieu de toutes celles attendues. Ou pire encore, lorsqu’il génère une erreur.

\n\n

Cette question sur Stack Overflow a recueilli des milliers de vues, et ce n’est pas pour rien. On pourrait penser que c’est simple : on passe un tableau, on obtient un résultat. Mais PDO (PHP Data Objects) est une chose stricte. Il ne sait pas « déplier » automatiquement les tableaux en une liste de valeurs pour IN(). Et c’est là que les choses deviennent intéressantes : les développeurs inventent un top des erreurs que nous allons analyser aujourd’hui. Et surtout, nous allons apprendre à les éviter.

\n\n

Dans cet article, nous ne nous contenterons pas de donner une solution toute faite. Nous allons parcourir le chemin allant du code incorrect au code sécurisé, performant et élégant. C’est parti !

\n\n

Erreur n°1 : Passage naïf d’un tableau via bindValue

\n\n

L’erreur la plus courante — essayer de fournir un tableau directement à bindValue().

\n\n
// ❌ INCORRECT\n$ids = [1, 2, 3, 5];\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN (?)");\n$stmt->bindValue(1, $ids, PDO::PARAM_STR);\n$stmt->execute();\n$users = $stmt->fetchAll();
\n\n

Que va-t-il se passer ? PHP convertit le tableau en chaîne "Array", et la requête devient WHERE id IN ("Array"). Résultat : zéro ligne ou une erreur, selon le pilote. PDO ne comprend tout simplement pas que le tableau doit être décomposé en espaces réservés individuels.

\n\n

Pourquoi cela se produit-il ? PDO attend une valeur scalaire (chaîne, nombre, null). Un tableau est un type composite et ne convient pas pour une liaison directe.

\n\n

Comment corriger ?

\n

Il faut créer manuellement des espaces réservés pour chaque élément du tableau. Pas de magie, seulement du code.

\n\n
// ✅ CORRECT\n$ids = [1, 2, 3, 5];\n$placeholders = implode(',', array_fill(0, count($ids), '?'));\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN ($placeholders)");\n$stmt->execute($ids);\n$users = $stmt->fetchAll();
\n\n

Ici, nous créons une chaîne de type ?,?,?,? et passons le tableau directement à execute(). PDO substituera les valeurs dans l’ordre. Simple et sécurisé.

\n\n

Erreur n°2 : Ignorer les types de données

\n\n

Supposons que vous utilisiez des espaces réservés nommés. Et là, la tentation est grande de faire ceci :

\n\n
// ❌ INCORRECT\n$ids = [1, 2, 3];\n$placeholders = [];\nforeach ($ids as $i => $id) {\n    $placeholders[] = ":id_$i";\n}\n$sql = "SELECT * FROM users WHERE id IN (" . implode(',', $placeholders) . ")";\n$stmt = $pdo->prepare($sql);\nforeach ($ids as $i => $id) {\n    $stmt->bindValue(":id_$i", $id, PDO::PARAM_INT);\n}\n$stmt->execute();
\n\n

Tout semble correct. Mais il y a un détail : si le tableau est vide, la requête devient WHERE id IN (), ce qui provoque une erreur de syntaxe SQL. Deuxième problème : si le tableau contient des chaînes et que vous avez spécifié PDO::PARAM_INT, PDO peut les convertir en zéro.

\n\n

Solution : vérifiez toujours si le tableau est vide et déterminez dynamiquement le type.

\n\n
// ✅ CORRECT\n$ids = [1, 2, 3];\nif (empty($ids)) {\n    return []; // ou lever une exception\n}\n$placeholders = [];\nforeach ($ids as $i => $id) {\n    $placeholders[] = ":id_$i";\n}\n$sql = "SELECT * FROM users WHERE id IN (" . implode(',', $placeholders) . ")";\n$stmt = $pdo->prepare($sql);\nforeach ($ids as $i => $id) {\n    $type = is_int($id) ? PDO::PARAM_INT : PDO::PARAM_STR;\n    $stmt->bindValue(":id_$i", $id, $type);\n}\n$stmt->execute();
\n\n

Erreur n°3 : Injection SQL via un échappement « manuel »

\n\n

Certains développeurs, désespérés, écrivent quelque chose comme

Blogs

Book Recommendations