__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() 魔术方法如何让你自定义克隆对象时发生的操作。
你需要将代码分布在两个文件中:
Document.php:创建一个Document类,用来表示有版本的文档。使用构造函数属性提升来定义公开的$title(字符串)、公开的$content(字符串)和公开的$version(整数,默认值为 1)。添加一个私有的$createdAt属性,用于存储当前时间戳(在构造函数中使用date('Y-m-d H:i:s'))。 你的文档需要包含:getCreatedAt():返回创建时间戳__clone():克隆文档时,版本号应增加 1,并将$createdAt重置为当前时间戳(克隆是“现在”创建的新版本)
main.php:引入 Document 文件。你会收到三个输入:标题、内容,以及作为克隆版本新内容的字符串。使用标题和内容创建一个Document。然后克隆它来创建一个新版本,并将克隆对象的内容更新为新内容字符串。 打印四行:- 原始文档的版本号
- 克隆文档的版本号
- 原始文档的内容
- 克隆文档的内容
这个系统展示了 __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. 克隆文档的内容
?>这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: PHP 在线编译器