«Posso vincular um array a uma condição IN()?» — a pergunta que atormenta um em cada dois desenvolvedores PHP
\n\nVocê já escreveu uma consulta como SELECT * FROM users WHERE id IN (?) e tentou passar um array via bindValue()? Se sim — você conhece bem aquela sensação de frustração quando o PDO retorna apenas uma linha em vez de todas as necessárias. Ou pior — lança um erro.
Esta pergunta no Stack Overflow acumulou milhares de visualizações, e não é à toa. Parece simples: passou o array, obteve o resultado. Mas o PDO (PHP Data Objects) é uma ferramenta rigorosa. Ele não sabe "desdobrar" arrays em uma lista de valores para IN() automaticamente. E é aí que começa a parte interessante: os desenvolvedores cometem os principais erros que vamos analisar hoje. E o principal — aprenderemos a evitá-los.
Neste artigo, não vamos apenas dar uma solução pronta. Vamos percorrer o caminho do código incorreto para o seguro, performático e elegante. Vamos lá!
\n\nErro nº1: Passagem ingênua do array via bindValue
\n\nO "ralo" mais comum — tentar passar o array diretamente para bindValue().
// ❌ INCORRETO\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\nO que acontece? O PHP converte o array em uma string "Array", e a consulta se torna WHERE id IN ("Array"). O resultado — zero linhas ou erro, dependendo do driver. O PDO simplesmente não entende que o array precisa ser desmembrado em placeholders individuais.
Por que isso acontece? O PDO espera um valor escalar (string, número, null). Um array é um tipo composto e não é adequado para binding direto.
\n\nComo corrigir?
\nÉ necessário criar manualmente placeholders para cada elemento do array. Sem mágica, apenas código.
\n\n// ✅ CORRETO\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\nAqui criamos uma string do tipo ?,?,?,? e passamos o array diretamente para execute(). O PDO substituirá os valores na ordem. Simples e seguro.
Erro nº2: Ignorar os tipos de dados
\n\nSuponha que você esteja usando placeholders nomeados. E surge a tentação de fazer assim:
\n\n// ❌ INCORRETO\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\nParece tudo certo. Mas há um detalhe: se o array estiver vazio, a consulta se torna WHERE id IN (), o que causa um erro de sintaxe SQL. O segundo problema — se o array contiver strings e você especificou PDO::PARAM_INT, o PDO pode convertê-las para zero.
Solução: sempre verifique se o array está vazio e determine o tipo dinamicamente.
\n\n// ✅ CORRETO\n$ids = [1, 2, 3];\nif (empty($ids)) {\n return []; // ou lançar uma exceção\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\nErro nº3: Injeção SQL através de "escape" manual
\n\nAlguns desenvolvedores, em desespero, escrevem algo como