نسخ المصفوفة في PHP: التعيين البسيط أم الاستنساخ؟

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

النسخ العميق للمصفوفة في 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\n
class 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() لنسخ مصفوفة:

\n\n
$original = [1, 2, 3];\n$copy = array_merge([], $original);\n$copy[0] = 100;\n\nprint_r($original); // [1, 2, 3]
\n\n

نعم، هذا يعمل. لكن هناك فارق دقيق مهم: array_merge() يعيد تعيين المفاتيح الرقمية ويعيد ترقيمها من الصفر. إذا كانت مفاتيحك عبارة عن سلاسل نصية، فكل شيء على ما يرام. أما إذا كانت رقمية، فكن مستعداً للمفاجآت:

\n\n
$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

الطريقة الأكثر موثوقية، ولكنها الأكثر إسهاباً، هي المرور عبر المصفوفة باستخدام حلقة:

\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)، فبدون العود

المدونات

توصيات الكتب