我不生产代码
我只是代码的搬运工

php使用 __clone() 复制对象

在PHP4中,复制对象只是简单地将一个变更赋值给另一个变量。

class CopyMe{}
$first = mew CopyMe();
$secoed = $first;
//PHP 4:$first 和 $second是两个完成相同的对象;
//PHP 5及以后的版本:first 和 $second指向同一对象;

      PHP 4 的这种用法可能会引起很多 bug,因为在变量赋值、调用方法、返回对象时都常常会无意中进行对象复制,而你可能并不知道。更糟的是,我们无法检测两个变量是否指定相同的对象。等值检测只会告诉你两者是否相等(==)或者两者是否相等且都是对象(===),但不会告诉你两者是否指向同一对象。

      在 PHP 中,对象的赋值和传递都是通过引用进行的。这意味着当我们之前的代码运行在 PHP 5 时,$first 和 $secoed 这两个变量包含指向同一个对象的引用,而没有各自保存一份相同的副本。这正是我们处理对象是所希望的,但有时候我们也需要获得一个对象的副本,而不是引用 。 

      PHP 提供了 clone 关键字来达到这个目的。clone 使用“值复制”的方式(key-value copy)新生成一个对象。

class CopyMe()
$first = mew Copy
Me();
$secoed = clone $first;
// PHP 5 及以后版本:$first 和 $secoed 现在是两个不同的对象

      但是这样的自制对象琮有问题。假如存在一个 Person 类,类里存在一个属性 $id。在实际开发过程中,$id 属性可能会与数据库中的某一条记录一一对应。如果允许复制 $id 属性,那么可能会有两个完全不同的对象指向数据库中的同一条记录,这显示不是我们想要的结果。此时对一个对象做的更新将会影响另一个,反之亦然。

      幸运的是,在对象上调用 clone 时,我们可以控制复制什么。我们可以通过实现一个特殊的方法 __clone 来达到这个目的。当在一个对象上调用 clone 关键字时,其 __clone 方法就会被调用。

      实现 __clone 方法时,要注意当前方法执行的环境。__clone 是在复制得到的对象上运行的,而不是在原始的对象上运行的。我们给 Person 类加上 __clone 方法:

class Person{
private $name;
private $age;
private $id;

public function __construct($name, $age){
$this->name = $name;
$this->age = $age;
}

public function setId($id){
$this->id = $id;
}

public function __clone(){
$this->id = 0;
}
}

      当在一个 Person 上调用 clone 时,产生一个新的副本,并且新副本的 __clone 方法会被调用,这意味着我们在 __clone 中重新赋值会生效。本例中,我们确保一个新副本对象的 $id 属性被重设为 0。

$person = new Person('bob', 44);
$person->setId(343);
$person2 = clone $person;
/*
   $person2:
     name:bob
     age:44
     id:0
*/

      这样的浅复制(shallow copy)可以保证所有基本数据类型的属性被完全复制。但是在复制对象属性时只复制引用,并不复制对象的引用,这可能并不是你所期望的。比如,我们给 Person 类添加一个 Account(帐户)对象类型的属性。这个对象有一个 $balance(余额)属性,可以将该属性也复制给对象新副本。但我们不希望原来的 Person 对象和复制得到的新对象具有对同一帐号的引用 。

class Account(){
public $balance;
public __construct($balance){
$this->balance = $balance;
}
}

class Person{
private $name;
private $age;
private $id;
public $account;

public function __construct($name, $age, Account $account){
$this->name = $name;
$this->age = $age;
$this->account = $account;
}

public function setId($id){
$this->id = $id;
}

public  function function __clone(){
$this->id = 0;
}
}

$person = new Perosn('bob', 44, new Account(200));
$person = setId(443);
$person2 = clone $person;

$person->account->balance +=10;   // 给 $person 充一些钱
print $person2->account->balance;  // 结果 $person2 也得到了这些钱,这不合理

输出:

210

$person 对象保存一个指向 Account 对象的引用,Account 对象也被标识为 public,这样比较方便(实际使用中最好限制一下属性的访问,在需要的时候再添加访问方法)。当创建新副本时,新对象中所保存的引用指向的是 $person 所引用的同一 Account 对象。上例中我们给 $person 对象的 Account 属性加钱,发现 $person2 对象中的余额也增加了。

如果不希望对象属性在复制之后共享,那么可以显示地在 __clone方法中复制指向的对象:

public function __clone(){
    $this->id = 0;
    $this->account = clone $this->account;
}


本文章为本站原创,如转载请注明文章出处:https://www.sviping.com/archives/18

分享到:
上一篇: 好好了解一下Cookie 下一篇: 关于git在windows中文件名不区分大小写的处理
12