استنساخ الكائنات PHP Object cloning
عملية استنساخ الكائنات PHP Object cloning، هي عمل نسخة من الكائن نفسه وليس مرجع له.
تخصيص المتغيرات Variables assignmentلفهم استنساخ الكائنات PHP Object cloning، سنرجع بالذاكرة للمتغيرات PHP Variables، حيث تعلمنا أنه عند تخصيص متغير إلى متغير آخر فإننا نقوم بتخصيص القيمة فقط وليس مرجع للمتغير PHP References، بحيث إذا قمنا بتغيير قيمة أحد المتغيران فلن يؤثر على الاآخر إلا إذا قمنا بعمل مرجع عن طريق العلامة &. لاحظ الكود التالي:
<?php $a=10; //Assign $a value to $b $b=$a; //Increase $b by 10 $b+=10; //Assign $b reference to $c $c = &$b; //This will print $b after adding 10 echo $b."<br>";//Outputs 20 //this will increase $b again by 5 and both $c and $b will point to this value (25) $c += 5; //echo all variables and check results echo $c."<br>";//Outputs 25 echo $b."<br>";//Outputs 25 echo $a;//Outputs 10 ?> تخصيص الكائنات Object assignmentلكن الأمر يختلف في الكائنات، فعند تخصيص كائن لآخر فكأنما قمنا بعمل مرجع للكائن الأصلي، وبالتالي أي تعديل على الكائن الجديد سيتم تطبيقه على الكائن الأصلي. لاحظ الكود التالي:
<?php class Person { public $firstName; public $lastName; } $person1 = new Person(); $person1->firstName="First Name"; $person1->lastName="Last Name"; $person2 = $person1; $person2->firstName="Surprise"; echo('<pre>'); var_dump($person1); var_dump($person2); echo('</pre>'); ?> قمنا بعمل كائن باسم person1 ثم قمنا بإعطاء قيم للخصائص firstname&lastname ثم قمنا بتخصيص الكائن person1 إلى كائن جديد person2 ثم قمنا بتغير قيمة الخاصية firstname في الكائن person2 إلى قيمة جديدة وعند فحص محتوى كلا الكائنين ستجد أن الكائن الأصلي أصبح يحتوي على نفس خصائص الكائن الجديد object(Person)#1 (2) { ["firstName"]=> string(8) "Surprise" ["lastName"]=> string(9) "Last Name" } object(Person)#1 (2) { ["firstName"]=> string(8) "Surprise" ["lastName"]=> string(9) "Last Name" }وهذا يؤكد أن الوضع الإفتراضي عند تخصيص كائن لآخر هو عمل مرجع للكائن الأصلي وسيتأثر بكل ما يقع على الكائن الجديد.
استنساخ الكائنات PHP Object cloningأحياناً لا يكون المطلوب عمل مرجع للكائن وإنما نسخة منه بحيث لا يتأثر الكائن الآخر بأي تعديلات على الكائن الجديد، وهنا يظهر مفهوم استنساخ الكائنات PHP Object cloning.
لعمل استنساخ للكائن يتم استخدام الكلمة clone، لاحظ الكود التالي:
<?php class Person { public $firstName; public $lastName; } $person1 = new Person(); $person1->firstName="First Name"; $person1->lastName="Last Name"; $person3 = clone $person1; $person3->firstName="Total Recall"; echo('<pre>'); var_dump($person1); var_dump($person3); echo('</pre>'); ?>حيث قمنا في السطر رقم 12 باستنساخ الكائن person1 عن طريق الكلمة clone، والآن لاحظ نتيجة الكود
object(Person)#1 (2) { ["firstName"]=> string(10) "First Name" ["lastName"]=> string(9) "Last Name" } object(Person)#2 (2) { ["firstName"]=> string(12) "Total Recall" ["lastName"]=> string(9) "Last Name" }تجد أن الكائن person1 لم يتأثر بما حدث إلى person3، ولكن هناك استثناء!!!
الإستنساخ السطحي Shallow object cloningماحدث في المثال السابق هو أننا قمنا بعمل نسخة فقط من خصائص الكائن الأصلي، أما إذا كان أحد الخصائص في الكائن الأصلي عبارة عن كائن آخر، فهنا هذه الخاصية مازالت قابلة للتعديل عليها لأنه سيتم التعامل معها كمرجع وليس كنسخة، وهذه العملية تسمى الإستنساخ السطحي Shallow object cloning. لاحظ الكود التالي:
<?php class Person { public $firstName; public $lastName; public $company; } class Company { public $companyName; } $companyA = new Company(); $companyA->companyName="PHP Training Co"; $person1 = new Person(); $person1->firstName="First Name"; $person1->lastName="Last Name"; $person1->company= $companyA; $person3 = clone $person1; $person3->firstName="Total Recall"; $person3->company->companyName="Awesome Co"; echo('<pre>'); var_dump($person1); var_dump($person3); echo('</pre>'); ?> قمنا بعمل اثنين كلاس person&company ثم أنشأنا كائن من company وهو companyA$ ثم قمنا بإعطاء قيمة للخاصية companyName ثم قمنا بإنشاء كائن من كلاس person وهو person1 ثم بدأنا في إعطاء القيم لخصائص الكائن person1 مع تخصيص الكائن companyA$ إلى الخاصية company ثم قمنا بعمل استنساخ للكائن person1 إلى person3 ثم بدأنا في محاولة تغيير كلاً من firstName و comapny. الآن لاحظ النتيجة object(Person)#2 (3) { ["firstName"]=> string(10) "First Name" ["lastName"]=> string(9) "Last Name" ["company"]=> object(Company)#1 (1) { ["companyName"]=> string(10) "Awesome Co" } } object(Person)#3 (3) { ["firstName"]=> string(12) "Total Recall" ["lastName"]=> string(9) "Last Name" ["company"]=> object(Company)#1 (1) { ["companyName"]=> string(10) "Awesome Co" } }نلاحظ أن قيمة firstName للكائن person1 لم تتغير كما نعرف، وإنما تغيرت قيمة الكائن المستنسخ، أما القيمة الخاصة بـ companyName تغيرت في كلا الكائنين، وهذا يؤكد ما ذكرناه عن الاستنساخ السطحي، وهو أن الكائنات الداخلية للكائن المستنسخ يتم التعامل معه كمراجع وتتأثر بكل مايتم فيه.
إذن ما العمل للتغلب على هذه المشكلة، إن كنت لاترغب في ذلك وتريد الحصول على نسخة صافية لجميع محتويات الكائن؟
هنا يظهر دور الدالة السحرية clone__، والتي يتم استدعاءها تلقائياً أثناء الاستساخ، وهنا يمكننا وضع الكود الذي سيسمح لنا باستنساخ الكائنات الداخلية للكائن المستنسخ هكذا:
<?php class Person { public $firstName; public $lastName; public $company; public function __clone(){ $this->company = clone $this->company; } } class Company { public $companyName; } $companyA = new Company(); $companyA->companyName="PHP Training Co"; $person1 = new Person(); $person1->firstName="First Name"; $person1->lastName="Last Name"; $person1->company= $companyA; $person3 = clone $person1; $person3->firstName="Total Recall"; $person3->company->companyName="Awesome Co"; echo('<pre>'); var_dump($person1); var_dump($person3); echo('</pre>'); ?>حيث قمنا بإضافة الدالة clone__ داخل الكلاس person، ثم قمنا بإضافة السطر التالي بداخلها
$this->company = clone $this->company;والذي يؤكد على أنه عند استنساخ كائن من الكلاس person يتم عمل استساخ أيضاً للخاصية company والتي نعرف أنها كائن من الكلاس company.
فإذا كانت الحالة الإفتراضية للإستنساخ (بدون clone__) هي الإستنساخ السطح Shallow object cloning، فإن هذه العملية تسمى بالاستساخ العميق Deep object cloning.
[highlight background=”” color=””]إذا كانت الدالة clone__ يتم استدعاءها بشكل تلقائي عند الاستنساخ فيمكن وضع أي شيء تريد تنفيذه أثناء الاستنساخ داخل الدالة[/highlight]
الإستنساخ العميق باستخدام الدالة serializeيمكن أيضاً عدم الإعتماد على الدالة clone__، وعمل استنساخ كامل عن طريق الستخدام الدالة serialize، لاحظ الكود التالي:
<?php class Person { public $firstName; public $lastName; public $company; } class Company { public $companyName; } $companyA = new Company(); $companyA->companyName="PHP Training Co"; $person1 = new Person(); $person1->firstName="First Name"; $person1->lastName="Last Name"; $person1->company= $companyA; $person3 = unserialize(serialize($person1)); $person3->firstName="Total Recall"; $person3->company->companyName="Awesome Co"; echo('<pre>'); var_dump($person1); var_dump($person3); echo('</pre>'); ?>لاحظ السطر رقم فالكود السابق يعطي نفس نتيجة clone__، ولكن يعيبه أنه ليس لك القدرة على التحكم فيما يمكن استنساخه وما لايمكن.
فضلاً لاتتردد في ترك استفساراتك في التعليقات فهذا يسعدني ويشعرني بالتفاعل الذي يدفعني دائماً لتقديم كل ما في الإمكان لنشر معلومة قيمة
وفقنا الله وإياكم 🙂

