مقدمة: "لماذا لا يفهم PDO المصفوفة الخاصة بي؟"
\nكل مطور PHP يعمل مع قواعد البيانات، سيواجه عاجلاً أم آجلاً موقفًا يحتاج فيه إلى تنفيذ استعلام باستخدام العامل IN(). قد يبدو الأمر بسيطًا: تمرير مصفوفة من القيم والحصول على البيانات المفلترة. لكن هنا يكمن المأزق الرئيسي.
إليك مثالًا تقليديًا يتبادر إلى ذهن المبتدئ:
\n<?php\n$ids = [1, 2, 3, 5];\n$stmt = $pdo->prepare("SELECT * FROM users WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // خطأ!\n?>\nوهنا يرمي PDO خطأ: "Array to string conversion" أو ببساطة يعيد نتيجة فارغة. لماذا يحدث هذا؟ لا يستطيع PDO "فك" المصفوفات في استعلامات SQL. إنه يتوقع قيمة مفردة لكل عنصر نائب مُسمى، وليس مصفوفة كاملة.
\nفي هذه المقالة، سنستعرض أهم الأخطاء التي يقع فيها المطورون عند محاولة ربط مصفوفة بـ IN()، ونعرض الحلول الصحيحة والآمنة والفعالة.
الخطأ رقم 1: تمرير المصفوفة مباشرة إلى execute()
\nهذا هو الخطأ الأكثر شيوعًا. يعتقد المطور أن PDO سيفهم تلقائيًا كيفية وضع كل قيمة من المصفوفة في شرط IN(). ولكن، كما اكتشفنا، PDO لا يعمل بهذه الطريقة.
كيف يبدو هذا في الكود
\n<?php\n$ids = [10, 20, 30];\n$stmt = $pdo->prepare("SELECT * FROM products WHERE id IN (:ids)");\n$stmt->execute([':ids' => $ids]); // خطأ فادح\n?>\nالنتيجة: خطأ أو تحذير حول تحويل المصفوفة إلى سلسلة نصية. حتى لو "مر" الكود، سيقوم PDO بإدراج كلمة "Array" في الاستعلام - وستحصل على WHERE id IN ('Array')، وهو أمر غير منطقي.
لماذا يحدث هذا؟ يتطلب PDO أن يقابل كل عنصر نائب قيمة واحدة. المصفوفة ليست قيمة، بل مجموعة من القيم. PDO لا يستطيع ولا يجب عليه أن يخمن ما تريد فعله.
\n\nالخطأ رقم 2: استخدام bindValue() مع مصفوفة
\nأحيانًا يحاول المطورون خداع PDO باستخدام 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); // خطأ!\n$stmt->execute();\n?>\nالمشكلة نفسها: bindValue() يتوقع قيمة مفردة. سيتم تحويل المصفوفة إلى سلسلة نصية "Array"، ولن يتم تنفيذ الاستعلام بشكل صحيح. المعامل الثالث (PDO::PARAM_STR) لا ينقذ الموقف - فهو يحدد نوع البيانات فقط، لكنه لا يغير منطق التعامل مع المصفوفة.
الخطأ رقم 3: الربط المباشر (حقن SQL)
\nبعد اليأس، يسلك بعض المطورين طريق المقاومة الأقل - ببساطة إدراج المصفوفة في الاستعلام عبر implode():
<?php\n$ids = [1, 2, 3];\n$ids_str = implode(',', $ids);\n$stmt = $pdo->query("SELECT * FROM users WHERE id IN ($ids_str)"); // خطير!\n?>\nما خطورة هذا؟ إذا كانت القيم في المصفوفة تأتي من المستخدم (مثل معاملات GET أو نموذج)، يمكن للمهاجم تمرير 1; DROP TABLE users; --. سيتحول الاستعلام إلى IN (1; DROP TABLE users; --)، وسيتم تدمير قاعدة البيانات الخاصة بك. هذا هو حقن SQL الكلاسيكي.
حتى لو كنت متأكدًا من أن البيانات آمنة، فهذه الممارسة سيئة للغاية. في يوم من الأيام ستنسى التحقق من مصدر البيانات، وستكون العواقب كارثية.
\n\nالحل الصحيح: العناصر النائبة الديناميكية
\nالآن إلى الأخبار الجيدة. يسمح PDO بإنشاء عناصر نائبة ديناميكيًا. تحتاج إلى توليد سلسلة نصية بالعدد المطلوب من ? أو :id1, :id2, ... وتمرير مصفوفة القيم بشكل منفصل.
الطريقة 1: استخدام العناصر النائبة الموضعية (?)
\nهذه هي الطريقة الأبسط والأكثر شيوعًا:
\n<?php\n