Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

__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)، أو إنشاء نسخ جديدة من الكائنات المتداخلة، أو إجراء أي عمليات تنظيف ضرورية ليكون الكائن المنسوخ مستقلاً تماماً.

challenge icon

التحدي

سهل

لنقم ببناء نظام لإصدارات المستندات يوضح كيف يقوم الاستنساخ (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. محتوى المستند المستنسخ

?>
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming