什么是PHP中的对象克隆?

39 php clone object

有人能解释我吗

  • 什么是PHP中的对象克隆?

  • 什么时候我应该在PHP中使用clone关键字?

Dec*_*ler 39

对象克隆是制作对象副本的行为.正如Cody指出的那样,PHP中的克隆是通过制作对象的浅表副本来完成的.这意味着克隆对象的内部对象将不会被克隆,除非您通过定义魔术方法显式地指示对象也克隆这些内部对象__clone().

如果不使用该__clone方法,则新对象的内部对象将引用内存中与克隆的原始对象的内部对象相同的对象.

考虑这些例子:

// in this exampe the internal member $_internalObject of both objects
// reference the same instance of stdClass in memory.
class CloneableClass
{
    private $_internalObject;

    public function __construct()
    {
        // instantiate the internal member
        $this->_internalObject = new stdClass();
    }
}

$classA = new CloneableClass();
$classB = clone $classA;


// in this exampe the internal member $_internalObject of both objects
// DON'T reference the same instance of stdClass in memory, but are inividual instances
class CloneableClass
{
    private $_internalObject;

    public function __construct()
    {
        // instantiate the internal member
        $this->_internalObject = new stdClass();
    }

    // on clone, make a deep copy of this object by cloning internal member;
    public function __clone()
    {
        $this->_internalObject = clone $this->_internalObject;
    }
}

$classA = new CloneableClass();
$classB = clone $classA;
Run Code Online (Sandbox Code Playgroud)

例如,用于克隆的用例是您不希望外部对象混淆对象的内部状态的情况.

假设您有一个具有内部对象地址的类User.

class Address
{
    private $_street;
    private $_streetIndex;
    private $_city;
    // etc...

    public function __construct( $street, $streetIndex, $city /* etc.. */ )
    {
        /* assign to internal values */
    }
}

class User
{
    // will hold instance of Address
    private $_address;

    public function __construct()
    {
        $this->_address = new Address( 'somestreet', '1', 'somecity' /* etc */ );
    }

    public function getAddress()
    {
        return clone $this->_address;
    }
}
Run Code Online (Sandbox Code Playgroud)

为了论证,假设您不希望外部对象弄乱用户对象的内部地址,但您确实希望能够为它们提供Address对象的副本.上面的例子说明了这一点.该getAddress方法将地址对象的克隆返回给调用对象.这意味着如果调用对象改变Address对象,则用户的内部地址不会改变.如果您没有给出克隆,那么外部对象能够更改用户的内部地址,因为默认情况下会提供引用,而不是克隆.

希望这一切都有道理.

PS.:
请注意,如果Address也有内部对象,那么你必须确保Address通过__clone()在Address中定义来克隆自己(根据我的第二篇文章).否则,你会感到头痛,试图找出你的数据被搞砸的原因.


lee*_*ers 27

克隆用于创建对象的真实副本.将对象分配给另一个变量不会创建副本 - 而是创建对与该对象相同的内存位置的引用:

<?php

$o= new stdclass;
$o->a= 'b';
$o->b= 'c';

$o2= $o;
$o2->a= 'd';

var_dump($o);
var_dump($o2);

$o3= clone $o;
$o3->a= 'e';
var_dump($o);
var_dump($o3);

?>
Run Code Online (Sandbox Code Playgroud)

此示例代码将输出以下内容:

object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#1 (2) {
  ["a"]=>
  string(1) "d"
  ["b"]=>
  string(1) "c"
}
object(stdClass)#2 (2) {
  ["a"]=>
  string(1) "e"
  ["b"]=>
  string(1) "c"
}
Run Code Online (Sandbox Code Playgroud)

  • @samayo:不,它也执行$ o3-&gt; b = $ o-&gt; b`。请记住,leepowers编写了一个简单的示例,但是在更复杂的场景中,拥有具有所有属性的副本的相同类型的对象(此后可以在不修改原始对象的情况下重新分配)的好处是巨大的。 (2认同)

Cod*_*nes 7

根据PHP 5,对象克隆就是所谓的"浅拷贝".然后它在被克隆的对象上调用__clone()方法.


cwe*_*ske 5

如果需要深度克隆 - 即子对象的克隆和孙对象的克隆 - 您可以__clone在每个类中覆盖,或者只是序列化+反序列化对象:

function deepClone($object)
{
    return unserialize(serialize($object));
}
Run Code Online (Sandbox Code Playgroud)