«هل يمكنني ربط مصفوفة بشرط IN()؟» — سؤال يقلق كل مطور PHP ثاني
\n\nهل سبق لك أن كتبت استعلامًا مثل SELECT * FROM users WHERE id IN (?) وحاولت تمرير مصفوفة إليه عبر bindValue()؟ إذا كان الأمر كذلك، فأنت بالتأكيد تعرف شعور الإحباط عندما يعيد PDO صفًا واحدًا فقط بدلاً من جميع الصفوف المطلوبة. أو الأسوأ من ذلك — يرمي خطأ.
هذا السؤال على Stack Overflow حصد آلاف المشاهدات، وليس من دون سبب. يبدو الأمر بسيطًا: مرر مصفوفة، احصل على النتيجة. لكن PDO (PHP Data Objects) شيء صارم. لا يستطيع «فرد» المصفوفات إلى قائمة قيم لـ IN() تلقائيًا. وهنا يبدأ الجزء المثير للاهتمام: يبتكر المطورون أفضل الأخطاء التي سنناقشها اليوم. والأهم من ذلك — سنتعلم كيفية تجنبها.
في هذه المقالة، لن نقدم حلاً جاهزًا فقط. سنسير في الطريق من الكود الخاطئ إلى الكود الآمن والفعال والجميل. هيا بنا!
\n\nالخطأ رقم 1: تمرير المصفوفة بسذاجة عبر bindValue
\n\nأكثر «مشكلة» شيوعًا — محاولة إطعام المصفوفة مباشرة إلى bindValue().
// ❌ غير صحيح\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ماذا سيحدث؟ سيحول PHP المصفوفة إلى سلسلة نصية "Array"، وسيتحول الاستعلام إلى WHERE id IN ("Array"). النتيجة — صفر صفوف أو خطأ، حسب برنامج التشغيل. PDO ببساطة لا يفهم أن المصفوفة يجب أن تتحلل إلى عناصر نائبة منفصلة.
لماذا يحدث هذا؟ يتوقع PDO قيمة قياسية (سلسلة نصية، رقم، null). المصفوفة هي نوع مركب، وهي غير مناسبة للربط المباشر.
\n\nكيفية الإصلاح؟
\nتحتاج إلى إنشاء عناصر نائبة يدويًا لكل عنصر من عناصر المصفوفة. لا سحر، فقط كود.
\n\n// ✅ صحيح\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هنا ننشئ سلسلة نصية بالشكل ?,?,?,? ونمرر المصفوفة مباشرة إلى execute(). سيقوم PDO بتعويض القيم بالترتيب. بسيط وآمن.
الخطأ رقم 2: تجاهل أنواع البيانات
\n\nلنفترض أنك تستخدم عناصر نائبة مسماة. وهنا يظهر الإغراء لفعل ذلك:
\n\n// ❌ غير صحيح\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يبدو كل شيء صحيحًا. لكن هناك فارق بسيط: إذا كانت المصفوفة فارغة، سيصبح الاستعلام WHERE id IN ()، مما يسبب خطأ نحويًا في SQL. المشكلة الثانية — إذا كانت المصفوفة تحتوي على سلاسل نصية، وحددت PDO::PARAM_INT، فقد يحولها PDO إلى صفر.
الحل: تحقق دائمًا من أن المصفوفة ليست فارغة وحدد النوع ديناميكيًا.
\n\n// ✅ صحيح\n$ids = [1, 2, 3];\nif (empty($ids)) {\n return []; // أو ارمِ استثناءً\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الخطأ رقم 3: حقن SQL عبر «التنقية» اليدوية
\n\nبعض المطورين في حالة يأس يكتبون شيئًا مثل