__clone واستنساخ الكائنات
جزء من قسم Object Oriented Programming في رحلة PHP على Coddy — الدرس 48 من 91.
عندما تقوم بتعيين كائن إلى متغير آخر في PHP، فإنك لا تنشئ نسخة - فكلا المتغيرين يشيران إلى نفس الكائن. لإنشاء نسخة مستقلة فعلية، تحتاج إلى استخدام الكلمة المفتاحية clone:
<?php
class Product {
public function __construct(
public string $name,
public float $price
) {}
}
$original = new Product("Laptop", 999.99);
$copy = clone $original;
$copy->price = 899.99;
echo $original->price . " - " . $copy->price;
المخرجات:
999.99 - 899.99بشكل افتراضي، يقوم PHP بإجراء نسخة سطحية (shallow copy) - حيث يقوم بنسخ جميع قيم الخصائص مباشرة. يعمل هذا بشكل جيد مع القيم القياسية (scalar values)، ولكن الكائنات الموجودة داخل كائنك ستظل تشير إلى نفس النسخة (instance). تتيح لك الطريقة السحرية __clone() تخصيص هذا السلوك:
<?php
class Order {
public function __construct(
public int $id,
public array $items = []
) {}
public function __clone(): void {
$this->id = 0;
$this->items = [];
}
}
$order1 = new Order(101, ["Book", "Pen"]);
$order2 = clone $order1;
echo "Original: " . $order1->id . ", Clone: " . $order2->id;
المخرجات:
Original: 101, Clone: 0يتم استدعاء الطريقة __clone() على الكائن الجديد بعد إجراء النسخ السطحي (shallow copy). هذا هو المكان الذي تقوم فيه بإعادة تعيين المعرفات (IDs)، أو إنشاء نسخ جديدة من الكائنات المتداخلة، أو إجراء أي عمليات تنظيف ضرورية ليكون الكائن المنسوخ مستقلاً تماماً.
التحدي
سهللنقم ببناء نظام لإصدارات المستندات يوضح كيف يقوم الاستنساخ (cloning) بإنشاء نسخ مستقلة، وكيف تتيح لك الطريقة السحرية __clone() تخصيص ما يحدث عند استنساخ كائن.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
Document.php— أنشئ فئةDocumentتمثل مستنداً ذا إصدارات. استخدم خاصية constructor promotion لتعريف$titleعام (string)، و$contentعام (string)، و$versionعام (integer، بقيمة افتراضية 1). أضف خاصية$createdAtخاصة تخزن الطابع الزمني الحالي (استخدمdate('Y-m-d H:i:s')في المنشئ/constructor). يحتاج المستند الخاص بك إلى:getCreatedAt()— تعيد الطابع الزمني للإنشاء__clone()— عند استنساخ مستند، يجب أن يزداد الإصدار (version) بمقدار 1، ويجب إعادة تعيين$createdAtإلى الطابع الزمني الحالي (المستنسخ هو إصدار جديد تم إنشاؤه "الآن")
main.php— قم بتضمين ملف Document. ستتلقى ثلاثة مدخلات: عنوان، ومحتوى، وسلسلة محتوى جديدة للنسخة المستنسخة. أنشئDocumentبالعنوان والمحتوى. ثم قم باستنساخه لإنشاء إصدار جديد، وقم بتحديث محتوى النسخة المستنسخة إلى سلسلة المحتوى الجديدة. اطبع أربعة أسطر:- إصدار المستند الأصلي
- إصدار المستند المستنسخ
- محتوى المستند الأصلي
- محتوى المستند المستنسخ
يوضح هذا النظام كيف تتيح لك __clone() التحكم فيما يحدث أثناء الاستنساخ. يحتفظ المستند الأصلي بإصداره ووقت إنشائه، بينما يصبح المستنسخ تلقائياً إصداراً جديداً بطابع زمني حديث — وهو أمر مثالي لتتبع تاريخ المستندات حيث يمثل كل مستنسخ مراجعة جديدة.
ورقة مرجعية
لإنشاء نسخة مستقلة من كائن، استخدم الكلمة المفتاحية clone:
<?php
class Product {
public function __construct(
public string $name,
public float $price
) {}
}
$original = new Product("Laptop", 999.99);
$copy = clone $original;
$copy->price = 899.99;
echo $original->price . " - " . $copy->price;
// المخرجات: 999.99 - 899.99
بشكل افتراضي، يقوم PHP بإجراء نسخ سطحي (shallow copy) لجميع قيم الخصائص. لتخصيص سلوك الاستنساخ، استخدم الطريقة السحرية __clone():
<?php
class Order {
public function __construct(
public int $id,
public array $items = []
) {}
public function __clone(): void {
$this->id = 0;
$this->items = [];
}
}
$order1 = new Order(101, ["Book", "Pen"]);
$order2 = clone $order1;
echo "Original: " . $order1->id . ", Clone: " . $order2->id;
// المخرجات: Original: 101, Clone: 0
يتم استدعاء الطريقة __clone() على الكائن الجديد بعد إجراء النسخ السطحي، مما يسمح لك بإعادة تعيين الخصائص أو إنشاء نسخ مستقلة من الكائنات المتداخلة.
جرّب بنفسك
<?php
require_once 'Document.php';
// قراءة المدخلات
$title = trim(fgets(STDIN));
$content = trim(fgets(STDIN));
$newContent = trim(fgets(STDIN));
// TODO: إنشاء مستند (Document) بالعنوان والمحتوى
// TODO: استنساخ المستند لإنشاء نسخة جديدة
// TODO: تحديث محتوى النسخة المستنسخة إلى سلسلة المحتوى الجديدة
// TODO: طباعة أربعة أسطر:
// 1. إصدار المستند الأصلي
// 2. إصدار المستند المستنسخ
// 3. محتوى المستند الأصلي
// 4. محتوى المستند المستنسخ
?>يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةمقدمة في OOPClasses مقابل Objectsالكلمة المفتاحية $thisالـ Methodsالـ Propertiesالمنشئ (__construct)الهادم (__destruct)مراجعة - آلة حاسبة بسيطة4الوراثة
الوراثة الأساسيةالكلمة المفتاحية parent::إعادة تعريف الدوال (Method Overriding)الكلمة المفتاحية finalالأصناف المجردة (Abstract Classes)مراجعة - هيكلية الموظفين7التغليف (Encapsulation)
العام، المحمي، الخاص (Public, Protected, Private)نظرة متعمقة على معدلات الوصولدوال Getters و Settersإخفاء المعلوماتميزة Constructor Promotion (8.0)مراجعة - نظام سجلات الطلاب10مفاهيم البرمجة كائنية التوجه المتقدمة
التركيب مقابل الوراثةحقن التبعيةالأصناف المجهولةالتعدادات (PHP 8.1)الألياف (PHP 8.1)تعمق في استنساخ الكائناتالمولدات والمكررات13أنماط التصميم - الجزء الثاني
نمط الأمرنمط المحولنمط المزيننمط طريقة القالبنمط الحالةنمط التركيبنمط المستودع2مساحات الأسماء والتحميل التلقائي
مقدمة إلى مساحات الأسماءالكلمة المفتاحية useمعيار التحميل التلقائي PSR-4أداة التحميل التلقائي Composerملخص - مشروع منظم5الواجهات والعقود
مقدمة إلى الواجهاتتنفيذ الواجهاتتنفيذ واجهات متعددةالواجهة مقابل الـ Abstract Classالـ Type Hinting مع الواجهاتملخص - حاسبة الأشكال8الميثودات السحرية (Magic Methods)
مقدمة في الميثودات السحرية__toString و __debugInfo__get, __set, __isset, __unset__call و __callStatic__clone واستنساخ الكائنات__serialize و __unserializeمراجعة - Custom Collection3خصائص الكلاس
خصائص الـ Instance مقابل الخصائص الساكنة (Static)الثوابت في الكلاساتالدوال والخصائص الساكنة (Static)الخصائص الخاصة (Private) والمحمية (Protected)خصائص للقراءة فقط (Readonly) في PHP 8.1مراجعة - مدير الحساب البنكي6تعدد الأشكال (Polymorphism)
مراجعة إعادة تعريف الدوال (Method Overriding)تعدد الأشكال عبر الـ Interfacesتلميح النوع (Type Hinting) وأنواع الاتحاد (Union Types)الربط الاستاتيكي المتأخر (Late Static Binding)ملخص - معالج الدفع (Payment Processor)