Menu
Coddy logo textTech

__clone и клонирование объектов

Часть раздела Объектно-ориентированное программирование путешествия по 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 выполняет поверхностное копирование — он копирует все значения свойств напрямую. Это хорошо работает для скалярных значений, но объекты внутри вашего объекта по-прежнему ссылаются на тот же экземпляр. Магический метод __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() вызывается для нового объекта после того, как была создана поверхностная копия. Здесь вы сбрасываете идентификаторы (ID), создаете новые копии вложенных объектов или выполняете любую очистку, необходимую для того, чтобы клон стал по-настоящему независимым.

challenge icon

Задание

Легко

Давайте создадим систему версионности документов, которая демонстрирует, как клонирование создает независимые копии и как магический метод __clone() позволяет настраивать действия, происходящие при клонировании объекта.

Вы организуете свой код в двух файлах:

  • Document.php — Создайте класс Document, который представляет документ с версиями. Используйте продвижение параметров конструктора (constructor promotion), чтобы определить публичное свойство $title (string), публичное $content (string) и публичное $version (integer, по умолчанию 1). Добавьте приватное свойство $createdAt, которое хранит текущую метку времени (используйте date('Y-m-d H:i:s') в конструкторе). Вашему документу необходимы:
    • getCreatedAt() — возвращает метку времени создания
    • __clone() — когда документ клонируется, версия должна увеличиться на 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 выполняет поверхностное копирование всех значений свойств. Чтобы настроить поведение при клонировании, используйте магический метод __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Проверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование