__clone とオブジェクトのクローン
CoddyのPHPジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 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のリセット、ネストされたオブジェクトの新しいコピーの作成、またはクローンを完全に独立させるために必要なクリーンアップ処理を行います。
チャレンジ
簡単クローニングによって独立したコピーがどのように作成されるか、そして __clone() マジックメソッドを使用してオブジェクトがクローンされる際の動作をどのようにカスタマイズできるかを示す、ドキュメント バージョニング システムを構築しましょう。
コードは2つのファイルに分けて構成します:
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 ファイルをインクルードします。タイトル、コンテンツ、およびクローンされたバージョン用の新しいコンテンツ文字列の3つの入力を受け取ります。タイトルとコンテンツを使用してDocumentを作成します。次に、それをクローンして新しいバージョンを作成し、クローンのコンテンツを新しいコンテンツ文字列に更新します。 次の4行を出力してください:- オリジナルのドキュメントのバージョン
- クローンされたドキュメントのバージョン
- オリジナルのドキュメントのコンテンツ
- クローンされたドキュメントのコンテンツ
このシステムは、__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: 次の4行を出力する:
// 1. オリジナルドキュメントのバージョン
// 2. 複製されたドキュメントのバージョン
// 3. オリジナルドキュメントの内容
// 4. 複製されたドキュメントの内容
?>このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。