我们已经在PHP设计模式的原型模式中讨论了PHP中对象复制的问题,这次算是回顾一下。原型模式可以看作是对象复制中的一个重要内容。在学习原型模式的时候,我们了解到对象中的引用变量,即当变量也是一个对象时,直接复制这个对象会导致其中的引用变量仍然指向同一个对象。是不是有点乱,我们举个例子来说明一下://clonemethodclasstestA{public$testValue;}classA{publicstatic$reference=0;公共$instanceReference=0;公共$t;公共函数__construct(){$this->instanceReference=++self::$reference;$this->t=newtestA();}publicfunction__clone(){$this->instanceReference=++self::$reference;$this->t=newtestA();}}$a1=newA();$a2=newA();$a11=clone$a1;$a22=$a2;var_dump($a11);//$instanceReference,3var_dump($a22);//$instanceReference,2$a1->t->testValue='nowa1';echo$a11->t->testValue,PHP_EOL;//''$a2->t->testValue='nowa2';echo$a22->t->testValue,PHP_EOL;//现在a2$a22->t->testValue='nowa22';echo$a2->t->testValue,PHP_EOL;//现在是a22//使用clone$a22=clone$a2;var_dump($a22);//$instanceReference,4$a2->t->testValue='nowisa2';echo$a22->t->testValue,PHP_EOL;//NULL$a22->t->testValue='nowa22';echo$a2->t->testValue,PHP_EOL;//现在是a2首先,通过变量的变化,我们可以看出使用clone关键字的对象拷贝会调用__clone()方法,这个神奇的方法是原型模式的核心。在这个方法中,我们可以重新实例化或者定义对象中的引用成员。通过clone,我们重新实例化$t变量,使$t成为一个新的对象,从而避免了引用带来的问题。在对象的复制中,需要特别注意递归引用的问题。即对象内部引用自己,会造成来回重复引用,形成递归死循环。//循环引用问题类B{public$that;function__clone(){//分段错误:11$this->that=clone$this->that;//$this->that=unserialize(serialize($this->that));//对象(B)#6(1){//["that"]=>//对象(B)#7(1){//["that"]=>//对象(B)#8(1){//["that"]=>//*RECURSION*无限递归//}//}//}}}$b1=newB();$b2=newB();$b1->that=$b2;$b2->that=$b1;$b3=clone$b1;var_dump($b3);即在B类中指向自己的实例,两个对象又指向对方,然后copy,就会出现这样的死循环。使用序列化和反序列化输出后,我们会看到RECURSION的引用提示。这是一个递归无限循环。必须尽可能避免这种情况。在上面的例子中,我们使用了序列化和反序列化来解决引用问题。对于对象拷贝的对象变量(对象变量中有更多层级的引用变量),该方法可以一次性解决最顶层对象__clone()方法中的引用问题。测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202001/source/%E5%85%B3%E4%BA%8EPHP%E4%B8%AD%E5%AF%B9%E8%B1%A1%E5%A4%8D%E5%88%B6%E7%9A%84%E9%82%A3%E7%82%B9%E4%BA%8B%E5%84%BF。PHP参考文档:https://www.php.net/manual/zh/languop5.cloning.php==========各媒体平台可搜索。【硬核项目经理】
