النسخ العميق للمصفوفة في PHP: الخرافات والواقع
\n\nتخيل موقفاً: أنت تكتب سكريبتاً يعالج بيانات من قاعدة البيانات. تحصل على مصفوفة من المستخدمين، وتقوم بشيء ما بها، ثم تكتشف فجأة أن البيانات الأصلية قد تغيرت. مألوف؟ إذا كنت تعمل مع PHP، فقد تفاجئك هذه المشكلة حتى المطورين ذوي الخبرة.
\n\nالسؤال «Is there a function to make a copy of a PHP array to another?» على Stack Overflow حصل على عشرات الآلاف من المشاهدات. وهذا ليس من قبيل الصدفة. للوهلة الأولى، يبدو أن نسخ المصفوفات في PHP مهمة تافهة. لكن الشيطان، كما هو الحال دائماً، يكمن في التفاصيل.
\n\nفي هذه المقالة، سنستعرض جميع الطرق الممكنة لنسخ مصفوفة في PHP: من التعيين البسيط إلى العمل مع الهياكل متعددة الأبعاد. ستتعرف متى يقوم PHP بإنشاء نسخة حقيقية، ومتى يقوم فقط بإنشاء مرجع للبيانات. هيا بنا!
\n\nالتعيين المباشر: المفاجأة الأولى
\n\nالطريقة الأكثر بديهية هي ببساطة تعيين المصفوفة لمتغير آخر:
\n\n$original = ['apple', 'banana', 'cherry'];\n$copy = $original;\n$copy[0] = 'mango';\n\nprint_r($original);\n// Array ( [0] => apple [1] => banana [2] => cherry )\nprint_r($copy);\n// Array ( [0] => mango [1] => banana [2] => cherry )\n\nالمدهش أن هذا يعمل! في PHP، المصفوفات هي value types (أنواع قيم)، وليست أنواعاً مرجعية. عندما تقوم بتعيين مصفوفة لمتغير آخر، يقوم PHP بإنشاء نسخة سطحية (shallow copy) مع نسخ مؤجل (copy-on-write).
\n\nبكلمات بسيطة: طالما أنك لا تغير أيًا من المصفوفات، فإنها تشير إلى نفس البيانات في الذاكرة. بمجرد أن تحاول تغيير عنصر في أحد المتغيرات، يقوم PHP بتخصيص ذاكرة جديدة ونسخ البيانات. هذا فعال جداً ولا يتطلب إجراءات إضافية.
\n\nالخلاصة: بالنسبة للمصفوفات البسيطة أحادية البعد، فإن التعيين المباشر هو نسخ كامل.
\n\nالمزلق: الكائنات داخل المصفوفة
\n\nوالآن تخيل أن المصفوفة تحتوي على كائنات:
\n\nclass User {\n public $name;\n public function __construct($name) {\n $this->name = $name;\n }\n}\n\n$original = [new User('Alice'), new User('Bob')];\n$copy = $original;\n$copy[0]->name = 'Charlie';\n\nprint_r($original[0]->name); // Charlie\n\nها هي المفاجأة! النسخة السطحية نسخت مرجع الكائن، وليس الكائن نفسه. بتغيير الكائن في النسخة، فإنك تغيره في الأصل. بالنسبة للكائنات، هناك حاجة إلى نهج مختلف.
\n\nدالة array_merge(): الفروق الدقيقة غير الواضحة
\n\nغالباً ما نرى نصيحة باستخدام array_merge() لنسخ مصفوفة:
$original = [1, 2, 3];\n$copy = array_merge([], $original);\n$copy[0] = 100;\n\nprint_r($original); // [1, 2, 3]\n\nنعم، هذا يعمل. لكن هناك فارق دقيق مهم: array_merge() يعيد تعيين المفاتيح الرقمية ويعيد ترقيمها من الصفر. إذا كانت مفاتيحك عبارة عن سلاسل نصية، فكل شيء على ما يرام. أما إذا كانت رقمية، فكن مستعداً للمفاجآت:
$original = ['a' => 1, 2 => 'two', 5 => 'five'];\n$copy = array_merge([], $original);\n\nprint_r($copy);\n// Array ( [a] => 1 [0] => two [1] => five )\n\nهل ترى؟ المفاتيح 2 و 5 تحولت إلى 0 و 1. هذا قد يكسر المنطق إذا كان ترتيب المفاتيح مهماً.
\n\nالخلاصة: array_merge() مناسب فقط لنسخ المصفوفات ذات المفاتيح النصية أو عندما لا تكون إعادة الفهرسة مشكلة.
النسخ باستخدام حلقة: التحكم اليدوي
\n\nالطريقة الأكثر موثوقية، ولكنها الأكثر إسهاباً، هي المرور عبر المصفوفة باستخدام حلقة:
\n\n$original = ['x' => 10, 'y' => 20];\n$copy = [];\n\nforeach ($original as $key => $value) {\n $copy[$key] = $value;\n}\n\n$copy['x'] = 100;\nprint_r($original['x']); // 10\n\nبالنسبة للمصفوفات البسيطة، هذا مبالغ فيه. ولكن إذا كنت بحاجة إلى عمل نسخ عميق (deep copy)، فبدون العود