PHP - 克隆对象


诸如 “$obj 1 = $obj 2” 之类 PHP 语句只是在内存中创建对同一对象的另一个引用。因此,属性的更改会同时反映在原始对象和复制对象中。PHP  clone 关键字创建对象的浅表副本。

 $obj2 = $obj1

原始对象中的更改不会反映在浅层副本中。

例子

请看下面的例子 -

<?php
   class foo {
      var $var1 = 'Hello';
   }
   $x = new foo();
   $y = $x;        # 参考副本
   echo $x->var1 . " " . $y->var1 . PHP_EOL;

   $x->var1 = "Hello World";
   echo $x->var1 . " " . $y->var1 . PHP_EOL;
?>

将产生以下输出 -

Hello Hello
Hello World Hello World

在第一种情况下,$y 只是 $x 的引用副本。因此,var1 属性的任何更改都会反映在两者中。

但是,如果我们将 $y 声明为 $x 的克隆,则原始对象中的任何更改都不会反映在其浅表副本中。

例子

请看下面的例子 -

<?php
   class foo {
      var $var1 = 'Hello World';
   }

   $x = new foo();

   # 浅拷贝
   $y = clone $x;
   echo $x->var1 . " " . $y->var1 . PHP_EOL;

   $x->var1 = "Hello PHP";
   echo $x->var1 . " " . $y->var1 . PHP_EOL;
?>

将产生以下输出 -

Hello World Hello World
Hello PHP Hello World

例子

在下面的代码中,myclass 具有属性名称作为地址类的对象。myclass 的对象通过赋值进行复制。其嵌入地址对象值的任何更改都会反映在这两个对象中,但 name 属性的更改不会在克隆对象中受到影响。

<?php
   class address {
      var $city="Nanded";
      var $pin="431601";
      function setaddr($arg1, $arg2) {
         $this->city=$arg1;
         $this->pin=$arg2;
      }
   }
   class myclass {
      var $name="Raja";
      var $obj;
      function setname($arg) {
         $this->name=$arg;
      }
   }

   $obj1=new myclass();
   $obj1->obj=new address();
   echo "原始对象\n";
   print_r($obj1);
   echo "\n";

   $obj2=$obj1;        # reference copy
   $obj1->setname("Ravi");
   $obj1->obj->setaddr("Mumbai", "400001");
   echo "变更后: 原始对象\n";
   print_r($obj1);
   echo "\n复制对象\n";
   print_r($obj2);
?>

将产生以下输出 -

原始对象
myclass Object
(
    [name] => Raja
    [obj] => address Object
        (
            [city] => Nanded
            [pin] => 431601
        )

)

变更后: 原始对象
myclass Object
(
    [name] => Ravi
    [obj] => address Object
        (
            [city] => Mumbai
            [pin] => 400001
        )

)

复制对象
myclass Object
(
    [name] => Ravi
    [obj] => address Object
        (
            [city] => Mumbai
            [pin] => 400001
        )

)

使用 “clone” 关键字

在浅拷贝中,原始对象的任何引用其他变量的属性都将保持引用。clone 关键字不会复制复制的对象。

现在,我们创建 myclass 对象的克隆,以便 $obj 2 是 $obj 1 的克隆。我们将 $obj 1 的 name 属性从 Raja 更改为 Ravi,并修改嵌入的地址对象。属性更改不会反映在其克隆中,但引用的地址对象将发生更改。

例子

请看下面的例子 -

<?php
   class address {
      var $city="Nanded";
      var $pin="431601";
      function setaddr($arg1, $arg2) {
         $this->city=$arg1;
         $this->pin=$arg2;
      }
   }
   class myclass {
      var $name="Raja";
      var $obj;
      function setname($arg) {
         $this->name=$arg;
      }
   }
   $obj1=new myclass();
   $obj1->obj=new address();
   echo "原始对象\n";
   print_r($obj1);
   echo "\n";

   $obj2=clone $obj1;        # 克隆拷贝
   $obj1->setname("Ravi");
   $obj1->obj->setaddr("Mumbai", "400001");
   echo "更改后:原始对象\n";
   print_r($obj1);
   echo "\n复制对象\n";
   print_r($obj2);
?>

将产生以下输出 -

原始对象
myclass Object
(
    [name] => Raja
    [obj] => address Object
        (
            [city] => Nanded
            [pin] => 431601
        )

)

更改后:原始对象
myclass Object
(
    [name] => Ravi
    [obj] => address Object
        (
            [city] => Mumbai
            [pin] => 400001
        )

)

复制对象
myclass Object
(
    [name] => Raja
    [obj] => address Object
        (
            [city] => Mumbai
            [pin] => 400001
        )

)

使用 __clone() 方法

clone 关键字创建对象的浅表副本。当一个对象被克隆时,PHP 将执行该对象所有属性的浅表复制。对其他变量的引用的任何属性都将保持引用。因此,对原始对象所做的任何更改也会显示在克隆的对象中。

如果你希望阻止复制的对象自动更新,我们需要使用 __clone() 方法创建对象的深层副本。它是 PHP 中的神奇方法之一。

克隆完成后,如果定义了 __clone() 方法,则将调用新创建的对象的 __clone() 方法,以允许需要更改的任何必要属性。

例子

在上面的例子中,我们有一个 myclass 的对象,它的一个属性 $obj 保存对 address 类对象的引用。为了实现深拷贝,我们覆盖了 myclass 中的 __clone() 魔术方法。

<?php
   class address {
      var $city="Nanded";
      var $pin="431601";
      function setaddr($arg1, $arg2) {
         $this->city=$arg1;
         $this->pin=$arg2;
      }
   }
   class myclass {
      var $name="Raja";
      var $obj;
      function setname($arg) {
         $this->name=$arg;
      }
      public function __clone() {
         $this->obj = clone $this->obj ;
      }
   }
   $obj1=new myclass();
   $obj1->obj=new address();
   echo "原始对象\n";
   print_r($obj1);
   echo "\n";

   $obj2=clone $obj1;        # 深度克隆拷贝
   $obj1->setname("Ravi");
   $obj1->obj->setaddr("Mumbai", "400001");
   echo "更改后:原始对象\n";
   print_r($obj1);
   echo "\n克隆对象\n";
   print_r($obj2);
?>

您现在将看到原始对象中的更改(我们更改了 address 属性)不会反映在克隆的对象中,如以下输出所示 -

原始对象
myclass Object
(
    [name] => Raja
    [obj] => address Object
        (
            [city] => Nanded
            [pin] => 431601
        )

)

更改后:原始对象
myclass Object
(
    [name] => Ravi
    [obj] => address Object
        (
            [city] => Mumbai
            [pin] => 400001
        )

)

克隆对象
myclass Object
(
    [name] => Raja
    [obj] => address Object
        (
            [city] => Nanded
            [pin] => 431601
        )

)