Menu
Coddy logo textTech

Глубокое погружение в клонирование объектов

Часть раздела Объектно-ориентированное программирование путешествия по PHP на Coddy — урок 61 из 91.

Вы узнали о магическом методе __clone ранее в этом курсе. Теперь давайте изучим критическую разницу между поверхностным и глубоким клонированием, когда объекты содержат другие объекты в качестве свойств.

По умолчанию PHP выполняет поверхностное копирование при клонировании. Скалярные значения дублируются, но свойства объектов остаются ссылками на те же самые объекты:

<?php
class Address {
    public function __construct(public string $city) {}
}

class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;

$clone->name = "Bob";
$clone->address->city = "London";

echo $original->name . " - " . $original->address->city;

Вывод:

Alice - London

Обратите внимание, что изменение $clone->name не повлияло на оригинал, но изменение города в адресе — повлияло! Оба объекта используют один и тот же экземпляр Address.

Чтобы создать глубокую копию, в которой вложенные объекты также клонируются, реализуйте __clone:

<?php
class Person {
    public function __construct(
        public string $name,
        public Address $address
    ) {}
    
    public function __clone(): void {
        $this->address = clone $this->address;
    }
}

$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";

echo $original->address->city;

Вывод:

Paris

Теперь каждый Person имеет свой собственный независимый Address. Когда ваши объекты содержат несколько вложенных объектов, вы должны клонировать каждый из них в __clone, чтобы достичь истинной независимости между оригиналом и копией.

challenge icon

Задание

Легко

Давайте создадим систему управления документами, которая демонстрирует разницу между поверхностным и глубоким клонированием. Вы создадите документы, содержащие информацию об авторе, и обеспечите, чтобы клонирование документа создавало по-настоящему независимую копию.

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

  • Author.php — Создайте класс Author, который представляет автора документа. Используйте продвижение параметров конструктора (constructor promotion) для определения публичного свойства $name (string) и публичного свойства $email (string).
  • Document.php — Создайте класс Document, который содержит автора. Подключите файл Author. Класс должен:
    • Использовать продвижение параметров конструктора для публичного свойства $title (string) и публичного свойства $author (Author)
    • Реализовать магический метод __clone() для выполнения глубокого клонирования — когда документ клонируется, его автор также должен быть клонирован, чтобы изменения автора клона не влияли на оригинал
    • Иметь метод getInfo(), который возвращает "[title] by [author name] ([author email])"
  • main.php — Подключите файл Document. Вы получите три входных значения: заголовок документа, имя автора и электронную почту автора.

    Создайте объект Document с объектом Author, используя предоставленные входные данные. Клонируйте документ, затем измените имя автора клона, добавив к нему " (Copy)", и измените заголовок клона, добавив в начало "Copy of ".

    Выведите две строки:

    • Информацию об оригинальном документе
    • Информацию о клонированном документе

    Если ваше глубокое клонирование реализовано правильно, автор оригинального документа должен остаться неизменным даже после изменения автора клона.

Эта задача демонстрирует, почему важно глубокое клонирование — без правильной реализации __clone() оба документа будут использовать один и тот же объект Author, и изменение одного из них повлияет на другой.

Попробуйте сами

<?php

require_once 'Document.php';

// Чтение входных данных
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));

// TODO: Создать автора с указанным именем и электронной почтой

// TODO: Создать документ с указанным заголовком и автором

// TODO: Клонировать документ

// TODO: Изменить имя автора клона, добавив к нему " (Copy)"

// TODO: Изменить заголовок клона, добавив в начало "Copy of "

// TODO: Вывести информацию об оригинальном документе

// TODO: Вывести информацию о клонированном документе

?>
quiz iconПроверьте себя

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

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