„Can I bind an array to an IN() condition?“ – eine Frage, die jeden zweiten PHP-Entwickler quält
\n\nHaben Sie jemals eine Abfrage wie SELECT * FROM users WHERE id IN (?) geschrieben und versucht, ein Array über bindValue() zu übergeben? Wenn ja, kennen Sie genau dieses Gefühl der Enttäuschung, wenn PDO nur eine einzige Zeile zurückgibt, anstatt aller benötigten. Oder noch schlimmer – einen Fehler wirft.
Diese Frage auf Stack Overflow hat tausende Aufrufe gesammelt, und das nicht ohne Grund. Es scheint so einfach: Array übergeben, Ergebnis erhalten. Aber PDO (PHP Data Objects) ist eine strenge Sache. Es kann Arrays nicht automatisch in eine Werteliste für IN() „entfalten“. Und hier beginnt das Interessante: Entwickler erfinden Top-Fehler, die wir heute analysieren. Und vor allem – lernen wir, sie zu vermeiden.
In diesem Artikel geben wir nicht nur eine fertige Lösung. Wir gehen den Weg von falschem Code zu sicherem, performantem und schönem Code. Los geht‘s!
\n\nFehler Nr. 1: Naive Übergabe eines Arrays über bindValue
\n\nDer häufigste „Stolperstein“ – der Versuch, ein Array direkt in bindValue() zu füttern.
// ❌ FALSCH\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\nWas passiert? PHP wandelt das Array in einen String "Array" um, und die Abfrage wird zu WHERE id IN ("Array"). Das Ergebnis – null Zeilen oder ein Fehler, abhängig vom Treiber. PDO versteht einfach nicht, dass das Array in einzelne Platzhalter aufgeteilt werden muss.
Warum passiert das? PDO erwartet einen skalaren Wert (String, Zahl, null). Ein Array ist ein zusammengesetzter Typ und eignet sich nicht für direktes Binden.
\n\nWie behebt man das?
\nMan muss manuell Platzhalter für jedes Element des Arrays erstellen. Keine Magie, nur Code.
\n\n// ✅ RICHTIG\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\nHier erstellen wir einen String der Form ?,?,?,? und übergeben das Array direkt an execute(). PDO setzt die Werte der Reihe nach ein. Einfach und sicher.
Fehler Nr. 2: Ignorieren von Datentypen
\n\nAngenommen, Sie verwenden benannte Platzhalter. Und dann kommt die Versuchung, Folgendes zu tun:
\n\n// ❌ FALSCH\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\nDas scheint alles richtig zu sein. Aber es gibt einen Haken: Wenn das Array leer ist, wird die Abfrage zu WHERE id IN (), was einen SQL-Syntaxfehler verursacht. Das zweite Problem – wenn das Array Strings enthält, Sie aber PDO::PARAM_INT angegeben haben, kann PDO diese auf null setzen.
Lösung: Überprüfen Sie immer das Array auf Leerheit und bestimmen Sie den Typ dynamisch.
\n\n// ✅ RICHTIG\n$ids = [1, 2, 3];\nif (empty($ids)) {\n return []; // oder eine Exception werfen\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\nFehler Nr. 3: SQL-Injection durch „manuelles“ Escaping
\n\nEinige Entwickler schreiben in ihrer Verzweiflung etwas wie