【问题标题】:How do I create a copy of an object in PHP?如何在 PHP 中创建对象的副本?
【发布时间】:2010-09-16 04:51:11
【问题描述】:

似乎在 PHP 中对象是通过引用传递的。甚至赋值运算符似乎也没有创建 Object 的副本。

这是一个简单的、人为的证明:

<?php

class A {
    public $b;
}


function set_b($obj) { $obj->b = "after"; }

$a = new A();
$a->b = "before";
$c = $a; //i would especially expect this to create a copy.

set_b($a);

print $a->b; //i would expect this to show 'before'
print $c->b; //i would ESPECIALLY expect this to show 'before'

?>

在这两种打印情况下,我都得到了“之后”

那么,我如何将 $a 通过值而不是引用传递给 set_b()

【问题讨论】:

  • 在极少数情况下,您实际上需要这种行为。因此,如果您发现自己经常使用它,那么您编写代码的方式可能存在更根本的问题?
  • 不,还不需要使用它。
  • (object) ((array) $objectA) 可能会为您带来相同的预期结果,但使用clone $objectAnew stdClass 时性能更好。
  • Re “即使赋值运算符似乎也没有创建对象的副本。” - 我希望不会!如果他们这样做了,结果将不再是 OO 语言(出于所有实际目的)。

标签: php copy clone shallow-copy


【解决方案1】:

根据文档 (http://ca3.php.net/language.oop5.cloning):

$a = clone $b;

【讨论】:

  • 根据文档,它只克隆不是对象的属性,因为对象属性只能通过引用复制。这意味着它不是深拷贝。
【解决方案2】:

在 PHP 5+ 中,对象是通过引用传递的。在 PHP 4 中,它们是按值传递的(这就是它在运行时通过引用传递的原因,这已被弃用)。

您可以使用 PHP5 中的“克隆”运算符来复制对象:

$objectB = clone $objectA;

此外,它只是通过引用传递的对象,而不是您在问题中所说的所有内容......

【讨论】:

  • 只想添加到正在阅读本文的任何人,克隆将保留对原始对象的引用。因此,使用克隆对象运行 MySQL 查询可能会产生不可预测的结果,因为执行可能不会以线性方式进行。
  • 纠正一个常见的误解(我认为即使是 PHP 文档也弄错了!) PHP 5 的对象不是“通过引用传递”的。与在 Java 中一样,它们具有 附加 级别的间接性 - 变量指向“对象指针”,并且指向一个对象。因此,两个变量可以指向同一个对象而不是对同一个值的引用。从这个例子可以看出:$a = new stdClass; $b =&amp; $a; $a = 42; var_export($b); 这里$b 是对变量 $a的引用;如果将=&amp; 替换为普通的=,则它不是 引用,仍然指向原始对象。
  • 运行时按引用传递是一个坏主意,因为它使函数调用的效果取决于函数的实现,而不是规范。它与默认值传递无关。
  • @Alex 你能详细说明你的评论吗? (在这里或其他地方。)你的观点有点不清楚 IMO。
  • @Ælex - re “克隆将保留对原始对象的引用” - 更准确地说,克隆对对象的属性进行副本。如果对象包含其他对象,那么这些嵌套对象现在将有两个对它们的引用。有时这正是我们想要的,有时这是一个问题。为了解决这个问题,对象类需要实现__clone(),如 Stanislav 的回答所示,并根据需要更正每个字段。
【解决方案3】:

答案在 Java 书籍中很常见。

  1. 克隆: 如果您不覆盖克隆方法,则默认行为是浅拷贝。如果您的对象只有原始成员变量,那完全可以。但是在以另一个对象作为成员变量的无类型语言中,这是一个令人头疼的问题。

  2. 序列化/反序列化

$new_object = unserialize(serialize($your_object))

这会根据对象的复杂程度以高成本实现深度复制。

【讨论】:

  • +1 很棒,很棒,用 PHP 进行深度复制的好方法,也很简单。让我问你一些关于 PHP clone 关键字提供的标准浅拷贝,你说只有原始成员变量被复制:PHP 数组/字符串是否被视为原始成员变量,所以它们被复制,对吗?
  • 对于任何人来说:一个“浅”的副本($a = clone $b,没有魔法__clone() 方法在起作用)相当于查看对象$b 的每个属性,并使用= 分配给同一类的新成员中的同一属性。作为对象的属性不会得到cloned,数组中的对象也不会;由引用绑定的变量也是如此;其他一切都只是一个值,并像任何赋值一样被复制。
  • 完美! json_decode(json_encode($obj));不克隆私有/受保护的属性和任何方法......反序列化(序列化也不克隆方法......
  • 太棒了!我终于摆脱了 PhpStorm 的错误; Call to method __clone from invalid context :)
  • 这会增加很多的运行时开销。您正在将对象序列化为字符串,然后将该字符串解析回新变量。虽然这可以完成您打算做的事情,但它以非常缓慢的方式进行。您不仅使用时间和内存将所有对象转换为字符串并返回,还破坏了 PHP 可能的 CopyOnWrite 机制。一个更好的方法是按照下面stackoverflow.com/a/186191/1614903 的建议正确实施您的__clone 方法。深入解释请参阅phpinternalsbook.com/php5/zvals/memory_management.html
【解决方案4】:

根据之前的评论,如果您有另一个对象作为成员变量,请执行以下操作:

class MyClass {
  private $someObject;

  public function __construct() {
    $this->someObject = new SomeClass();
  }

  public function __clone() {
    $this->someObject = clone $this->someObject;
  }

}

现在您可以进行克隆了:

$bar = new MyClass();
$foo = clone $bar;

【讨论】:

    【解决方案5】:

    如果您想在不同实例中完全复制对象的属性,您可能需要使用此技术:

    将其序列化为 JSON,然后将其反序列化回 Object。

    【讨论】:

    • 嗯,我会像地狱一样避免它。
    【解决方案6】:

    这段代码帮助克隆方法

    class Foo{
    
        private $run=10;
        public $foo=array(2,array(2,8));
        public function hoo(){return 5;}
    
    
        public function __clone(){
    
            $this->boo=function(){$this->hoo();};
    
        }
    }
    $obj=new Foo;
    
    $news=  clone $obj;
    var_dump($news->hoo());
    

    【讨论】:

    • 这段代码有点没用,即使你去掉 __clone 方法它也能工作:)
    【解决方案7】:

    我正在做一些测试并得到了这个:

    class A {
      public $property;
    }
    
    function set_property($obj) {
      $obj->property = "after";
      var_dump($obj);
    }
    
    $a = new A();
    $a->property = "before";
    
    // Creates a new Object from $a. Like "new A();"
    $b = new $a;
    // Makes a Copy of var $a, not referenced.
    $c = clone $a;
    
    set_property($a);
    // object(A)#1 (1) { ["property"]=> string(5) "after" }
    
    var_dump($a); // Because function set_property get by reference
    // object(A)#1 (1) { ["property"]=> string(5) "after" }
    var_dump($b);
    // object(A)#2 (1) { ["property"]=> NULL }
    var_dump($c);
    // object(A)#3 (1) { ["property"]=> string(6) "before" }
    
    // Now creates a new obj A and passes to the function by clone (will copied)
    $d = new A();
    $d->property = "before";
    
    set_property(clone $d); // A new variable was created from $d, and not made a reference
    // object(A)#5 (1) { ["property"]=> string(5) "after" }
    
    var_dump($d);
    // object(A)#4 (1) { ["property"]=> string(6) "before" }
    
    ?>
    

    【讨论】:

      【解决方案8】:

      只是为了澄清 PHP 在写入时使用复制,所以基本上所有内容都是参考,直到你修改它,但对于对象,你需要使用 clone 和 __clone() 魔术方法,就像在接受的答案中一样。

      【讨论】:

        【解决方案9】:

        在本例中,我们将创建 iPhone 类并通过 克隆

        从中复制
        class iPhone {
        
        public $name;
        public $email;
        
            public function __construct($n, $e) {
        
               $this->name = $n;
               $this->email = $e;
        
            }
        }
        
        
        $main = new iPhone('Dark', 'm@m.com');
        $copy = clone $main;
        
        
        // if you want to print both objects, just write this    
        
        echo "<pre>"; print_r($main);  echo "</pre>";
        echo "<pre>"; print_r($copy);  echo "</pre>";
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-15
          • 2016-08-08
          相关资源
          最近更新 更多