Menu
Coddy logo textTech

オブジェクトのクローンを徹底解説

CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 61/91。

このコースの前半で、__clone magic method について学びました。ここでは、オブジェクトがプロパティとして他のオブジェクトを含む場合の、shallow cloning と deep cloning の重要な違いについて見ていきましょう。

デフォルトでは、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 を変更しても original には影響しませんでしたが、住所の city を変更すると影響しました。両方のオブジェクトが同じ Address インスタンスを共有しています。

ネストされたオブジェクトもクローンされるdeep コピーを作成するには、__cloneを Implement します。

<?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 を持ちます。オブジェクトに複数のネストされたオブジェクトが含まれている場合、original とコピーの間で真の独立性を実現するには、__clone 内でそれぞれを clone する必要があります。

challenge icon

チャレンジ

簡単

浅い cloning と deep cloning の違いを示す document management system を構築しましょう。author 情報を含む document を作成し、document の cloning によって本当に独立した Copy が作成されることを確認します。

コードを3つのファイルに整理します。

  • Author.php:document の author を表す Author class を Create します。constructor promotion を使用して、public $name(string)と public $email(string)を define します。
  • Document.php:author を含む Document class を Create します。Author file を Include します。class では次のことを行います。
    • public $title(string)と public $author(Author)に constructor promotion を使用する
    • __clone() magic method を Implement して deep cloning を実行する。document が cloned されたとき、その author も cloned されるため、clone の author への変更が original に影響しない
    • getInfo() method を持ち、"[title] by [author name] ([author email])" を returns する
  • main.php:Document file を Include します。3つの inputs、つまり document の title、author の name、author の email を受け取ります。

    provided inputs を使用して Author とともに Document を Create します。document を clone し、clone の author name に " (Copy)" を appending して Modify し、clone の title の先頭に "Copy of " を prepending して Modify します。

    2行を Print します。

    • original document の info
    • cloned document の info

    deep cloning が正しく Implement されていれば、clone の author を Modify した後も、original document の author は変更されない should です。

この challenge は、deep cloning が重要である理由を示します。__clone() の適切な Implement がなければ、両方の document が同じ Author object を共有し、一方を変更するともう一方にも影響します。

自分で試してみよう

<?php

require_once 'Document.php';

// 入力を読み取る
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));

// TODO: 提供された名前とメールで Author を作成する

// TODO: 提供されたタイトルと Author で Document を作成する

// TODO: ドキュメントをクローンする

// TODO: クローンの著者名の末尾に " (Copy)" を追加して変更する

// TODO: クローンのタイトルの先頭に "Copy of " を追加して変更する

// TODO: 元のドキュメントの情報を出力する

// TODO: クローンしたドキュメントの情報を出力する

?>
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: PHPオンラインコンパイラ