# 对象复制

在多数情况下，我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要：如果第一个对象中保存着第二个对象的引用，当你复制第一个对象时，你想使用的对象不再是第二个对象而是它的一个副本，那么你必须得到第二个对象的一个副本。对象复制可以通过 `clone` 关键字来完成（如果可能，这将调用对象的 `__clone()` 方法）。对象中的 `__clone()` 方法不能被直接调用。当对象被复制后， PHP 会对对象的所有属性执行一个浅复制。所有的引用属性仍然会是一个指向原来的变量的引用。

```php
<?php

class Cat
{
    /**
     * The name of cat.
     *
     * @var string
     */
    public string $name;

    /**
     * The age of cat.
     *
     * @var int
     */
    public int $age;

    /**
     * Store the name and age.
     *
     * @param  string $name
     * @param  int    $age
     * @return void
     */
    public function __construct(string $name, int $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}

class Dog
{
    /**
     * The name of dog.
     *
     * @var string
     */
    public string $name;

    /**
     * Store an instance of cat.
     *
     * @var Cat
     */
    public Cat $cat;
}

$spike = new Dog();
$spike->name = 'Spike';
$spike->cat = new Cat('Tom', 6);
$tyke = clone $spike;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }

$tyke->name = 'Tyke';
$tyke->cat->age = 2;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(2) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(4) "Tyke" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(2) } }

```

当复制完成时，如果定义了 `__clone()` 方法，则新创建的对象（复制生成的对象）中的 `__clone()` 方法会被调用，可用于修改属性的值（如果有必要的话）。

```php
<?php

class Cat
{
    /**
     * The name of cat.
     *
     * @var string
     */
    public string $name;

    /**
     * The age of cat.
     *
     * @var int
     */
    public int $age;

    /**
     * Store the name and age.
     *
     * @param  string $name
     * @param  int    $age
     * @return void
     */
    public function __construct(string $name, int $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}

class Dog
{
    /**
     * The name of dog.
     *
     * @var string
     */
    public string $name;

    /**
     * Store an instance of cat.
     *
     * @var Cat
     */
    public Cat $cat;

    /**
     * Complete deep copy.
     *
     * @param  void
     * @return void
     */
    public function __clone()
    {
        $this->cat = clone $this->cat;
    }
}

$spike = new Dog();
$spike->name = 'Spike';
$spike->cat = new Cat('Tom', 6);
$tyke = clone $spike;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }

$tyke->name = 'Tyke';
$tyke->cat->age = 2;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(4) "Tyke" ["cat"]=> object(Cat)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(2) } }

```

可以将对象序列化后再反序列化来完成对象的深复制。

```php
<?php

class Cat
{
    /**
     * The name of cat.
     *
     * @var string
     */
    public string $name;

    /**
     * The age of cat.
     *
     * @var int
     */
    public int $age;

    /**
     * Store the name and age.
     *
     * @param  string $name
     * @param  int    $age
     * @return void
     */
    public function __construct(string $name, int $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}

class Dog
{
    /**
     * The name of dog.
     *
     * @var string
     */
    public string $name;

    /**
     * Store an instance of cat.
     *
     * @var Cat
     */
    public Cat $cat;
}

$spike = new Dog();
$spike->name = 'Spike';
$spike->cat = new Cat('Tom', 6);
$tyke = unserialize(serialize($spike));
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }

$tyke->name = 'Tyke';
$tyke->cat->age = 2;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(Dog)#3 (2) { ["name"]=> string(4) "Tyke" ["cat"]=> object(Cat)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(2) } }

```

还可以将对象进行 `JSON` 编码后再解码来完成对象的深复制。

```php
<?php

class Cat
{
    /**
     * The name of cat.
     *
     * @var string
     */
    public string $name;

    /**
     * The age of cat.
     *
     * @var int
     */
    public int $age;

    /**
     * Store the name and age.
     *
     * @param  string $name
     * @param  int    $age
     * @return void
     */
    public function __construct(string $name, int $age)
    {
        $this->name = $name;
        $this->age = $age;
    }
}

class Dog
{
    /**
     * The name of dog.
     *
     * @var string
     */
    public string $name;

    /**
     * Store an instance of cat.
     *
     * @var Cat
     */
    public Cat $cat;
}

$spike = new Dog();
$spike->name = 'Spike';
$spike->cat = new Cat('Tom', 6);
$tyke = json_decode(json_encode($spike));
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(stdClass)#3 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(stdClass)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }

$tyke->name = 'Tyke';
$tyke->cat->age = 2;
var_dump($spike); // object(Dog)#1 (2) { ["name"]=> string(5) "Spike" ["cat"]=> object(Cat)#2 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(6) } }
var_dump($tyke);  // object(stdClass)#3 (2) { ["name"]=> string(4) "Tyke" ["cat"]=> object(stdClass)#4 (2) { ["name"]=> string(3) "Tom" ["age"]=> int(2) } }

```

