Detailed Explanation of php5 Object Replication clone Shallow Replication and Deep Replication Instances

  • 2021-12-13 16:35:13
  • OfStack

This article illustrates php5 object replication, clone, shallow replication, and deep replication. Share it for your reference, as follows:

The origin of object replication

Why objects have the concept of "replication" is closely related to the way objects pass values in PHP5. Let's take a look at the following simple code

PHP code


/**
*  TV sets 
*/
class Television
{
  /**
    Screen height 
   */
  protected $_screenLength = 300;
  /**
    Screen width 
   */
  protected $_screenHight = 200;
  /**
    Appearance color of TV set 
   */
  protected $_color    = 'black';
  /**
    Return to TV Appearance Color 
   */
  public function getColor()
  {
    return $this->_color;
  }
  /**
    Set the TV appearance color 
   */
  public function setColor($color)
  {
    $this->_color = (string)$color;
    return $this;
  }
}
$tv1 = new Television();
$tv2 = $tv1;

This code defines a TV class Television, $tv1 is an example of a TV, and then we assign the value of $tv1 to $t2 according to the common variable assignment method. So now we have two televisions $tv1 and $tv2, is that true? Let's test 1.

PHP code


echo 'color of tv1 is: ' . $tv1->getColor();//tv1 The color of is black
echo '<br>';
echo 'color of tv2 is: ' . $tv2->getColor();//tv2 The color of is black
echo '<br>';
// Put tv2 To paint white 
$tv2->setColor('white');
echo 'color of tv2 is: ' . $tv2->getColor();//tv2 The color of is white
echo '<br>';
echo 'color of tv1 is: ' . $tv1->getColor();//tv1 The color of is white

First of all, we saw that the colors of tv1 and tv2 are black. Now we want tv2 to change its color, so we set its color to white. When we look at the color of tv2 again, it really becomes white, which seems to meet our requirements, but it is not as smooth as imagined. When we continue to look at the color of tv1, we find that tv1 has also changed from black to white. We didn't reset the color of tv1. Why did tv1 change from black to white? This is because in PHP5, objects are assigned and passed in a "reference" way. PHP5 uses an Zend engine, II, and objects are stored in a separate structure, Object, Store, rather than in Zval like other paradigms (in PHP4, objects and paradigms are stored in Zval). In Zval, only pointers to objects are stored, not contents (value). When we copy an object or pass an object as an argument to a function, we don't need to copy the data. Just keep the same object pointer and notify the Object Store that this particular object now points to by another zval. Since the object itself is located on Object Store, any changes we make to it will affect all zval structures that hold pointers to the object--as in the program, any changes to the target object will affect the source object. This makes the PHP object look as if it is always passed by reference (reference). Therefore, the above tv2 and tv1 actually point to the same TV instance, and our operations on tv1 or tv2 are actually aimed at the same instance. So our "replication" failed. It seems that direct variable assignment can't copy objects, so PHP5 provides an operation dedicated to copying objects, that is, clone. This is the origin of object replication.

Copy objects with clone (cloning)

We now use the clone language structure of PHP5 to copy the object, and the code is as follows:

PHP code


$tv1 = new Television();
$tv2 = clone $tv1;
echo 'color of tv1 is: ' . $tv1->getColor();//tv1 The color of is black
echo '<br>';
echo 'color of tv2 is: ' . $tv2->getColor();//tv2 The color of is black
echo '<br>';
// Put tv2 Color it white instead 
$tv2->setColor('white');
echo 'color of tv2 is: ' . $tv2->getColor();//tv2 The color of is white
echo '<br>';
echo 'color of tv1 is: ' . $tv1->getColor();//tv1 The color of is black

In line 2 of this code, we copied tv1 with the clone keyword, and now that we have a copy of the real tv1, tv2, we still use the previous method to check whether the copy was successful. We can see that we changed the color of tv2 to white, and the color of tv1 was still black, so our copy operation was successful.

__clone magic method

Now we consider such a situation that every TV set should have its own number, which should be the only one like our ID number 1, so when we copy a TV set, we don't want this number to be copied, so as not to cause some trouble. One strategy we thought of was to empty the assigned TV number, and then reassign the number according to the demand.

Then the __clone magic method is specifically designed to solve this problem, and the __clone magic method will be triggered when the object is copied (that is, clone operation). We modified the code of the TV class Television, adding the numbering attribute and the __clone method. The code is as follows.

PHP code


/**
 TV sets 
*/
class Television
{
  /**
    Television number 
   */
  protected $_identity  = 0;
  /**
    Screen height 
   */
  protected $_screenLength = 300;
  /**
    Screen width 
   */
  protected $_screenHight = 200;
  /**
    Appearance color of TV set 
   */
  protected $_color    = 'black';
  /**
    Return to TV Appearance Color 
   */
  public function getColor()
  {
    return $this->_color;
  }
  /**
    Set the TV appearance color 
   */
  public function setColor($color)
  {
    $this->_color = (string)$color;
    return $this;
  }
  /**
    Return TV number 
   */
  public function getIdentity()
  {
    return $this->_identity;
  }
  /**
    Set the TV number 
   */
  public function setIdentity($id)
  {
    $this->_identity = (int)$id;
    return $this;
  }
  public function __clone()
  {
    $this->setIdentity(0);
  }
}

Let's copy such a TV object.

PHP code


$tv1 = new Television();
$tv1->setIdentity('111111');
echo 'id of tv1 is ' . $tv1->getIdentity();//111111
echo '<br>';
$tv2 = clone $tv1;
echo 'id of tv2 is ' . $tv2->getIdentity();//0

We produced a TV set tv1 and set its number to 111111. Then we copied tv1 with clone to get tv2. At this time, the magic method of __clone was triggered, which will directly act on the copied object tv2. We called setIdentity member method in __clone method to empty the _ identity attribute of tv2 so that we can renumber it later. From this, we can see that the __clone magic method makes it very convenient for us to do some additional operations when using clone objects.

Fatal flaw in clone operation

Can clone really achieve the ideal replication effect? In some cases, you should find that the clone operation is not as perfect as we thought. We will modify the above TV category by 1, and then do the test.

Every TV will come with a remote control, so we will have a remote control class. The remote control and TV are a "aggregation" relationship (compared with the "combination" relationship, it is a weak dependence relationship, because the TV can work normally even without remote control in 1 case). Now our TV objects should all hold a reference to the remote control object. Let's take a look at the code

PHP code


/**
 TV sets 
*/
class Television
{
  /**
    Television number 
   */
  protected $_identity  = 0;
  /**
    Screen height 
   */
  protected $_screenLength = 300;
  /**
    Screen width 
   */
  protected $_screenHight = 200;
  /**
    Appearance color of TV set 
   */
  protected $_color    = 'black';
  /**
    Remote control object 
   */
  protected $_control   = null;
  /**
    Load the remote control object in the constructor 
   */
  public function __construct()
  {
    $this->setControl(new Telecontrol());
  }
  /**
    Setting Remote Control Objects 
   */
  public function setControl(Telecontrol $control)
  {
    $this->_control = $control;
    return $this;
  }
  /**
    Returns the remote control object 
   */
  public function getControl()
  {
    return $this->_control;
  }
  /**
    Return to TV Appearance Color 
   */
  public function getColor()
  {
    return $this->_color;
  }
  /**
    Set the TV appearance color 
   */
  public function setColor($color)
  {
    $this->_color = (string)$color;
    return $this;
  }
  /**
    Return TV number 
   */
  public function getIdentity()
  {
    return $this->_identity;
  }
  /**
    Set the TV number 
   */
  public function setIdentity($id)
  {
    $this->_identity = (int)$id;
    return $this;
  }
  public function __clone()
  {
    $this->setIdentity(0);
  }
}
/**
 Remote control class 
*/
class Telecontrol
{
}

Copy such a TV object and observe the remote control object of the TV.

PHP code


$tv1 = new Television();
$tv2 = clone $tv1;
$contr1 = $tv1->getControl(); // Get tv1 Remote control of contr1
$contr2 = $tv2->getControl(); // Get tv2 Remote control of contr2
echo $tv1;  //tv1 Adj. object id  For  #1
echo '<br>';
echo $contr1; //contr1 Adj. object id  For #2
echo '<br>';
echo $tv2;  //tv2 Adj. object id  For  #3
echo '<br>';
echo $contr2; //contr2 Adj. object id  For #2

After copying, we look at the object id, and tv2 is copied from tv1 by clone operation. The objects id of tv1 and tv2 are 1 and 3 respectively, which means that tv1 and tv2 refer to two different TV objects, which is consistent with the result of clone operation. Then we get the remote control object contr1 of tv1 and the remote control object contr2 of tv2, By looking at their object id, we find that the object id of contr1 and contr2 is both 2, which indicates that they are references to the same object. That is to say, although we copied tv2 from tv1, the remote control has not been copied. Every TV set should be equipped with a remote control, and tv2 and tv1 share a remote control here, which is obviously unreasonable.

It can be seen that clone operation has such a big defect: when using clone operation to copy objects, when the copied object has references to other objects, the referenced objects will not be copied. However, this situation is very common. Nowadays, "composition/aggregation reuse" is advocated to replace "inheritance reuse". "Composition" and "aggregation" are methods to make one object have a reference to another object, so as to reuse the referenced object. We should consider this situation when using clone. So how should we solve such a defect when we are in clone object? Perhaps you quickly thought of the __clone magic method mentioned earlier, which is indeed a solution.

Scheme 1: Make up with __clone magic method

We have already introduced the use of the __clone magic method, in which we can re-reference the references of other objects in the copied object to a new object. Let's take a look at the modified __clone () magic method:

PHP code


public function __clone()
{
  $this->setIdentity(0);
  // Reset 1 Remote control objects 
  $this->setControl(new Telecontrol());
}

In line 04, we reset a remote controller for the copied TV object. When we look at the id of the object according to the previous method, we can find that the remote controllers of the two TV sets have different id, so our problem is solved.

However, this method is probably not very good. If there are multiple references to other objects in the copied object, we must reset them one by one in the __clone method. Worse still, if the class of the copied object is provided by a third party and we cannot modify the code, the copying operation will basically not be successfully completed.

We use clone to copy objects. This copy is called "shallow copy": All variables of the copied object have the same values as the original object, while all references to other objects still point to the original object. That is, shallow replication only replicates the object under consideration, not the object it references. Compared with "shallow copy", there is also a "deep copy": all variables of the copied object have the same values as the original object, except those that refer to other objects. That is to say, deep copy copies all the objects referenced by the object to be copied once. Deep replication needs to decide how many layers to go into, which is a difficult problem to determine. In addition, there may be circular references, which must be handled with care. Our Solution 2 will be a deep replication solution.

Scenario 2: Deep replication using serialization

PHP has serialization (serialize) and de-serialization (unserialize) functions, so we just need to write an object to a stream with serialize (), then read the object back from the stream, and the object is copied. In JAVA, this process is called "freezing" and "thawing". Next, we will test this method under 1:

PHP code


$tv1 = new Television();
$tv2 = unserialize(serialize($tv1));// Serialize and then deserialize 
$contr1 = $tv1->getControl(); // Get tv1 Remote control of contr1
$contr2 = $tv2->getControl(); // Get tv2 Remote control of contr2
echo $tv1;  //tv1 Adj. object id  For  #1
echo '<br>';
echo $contr1; //contr1 Adj. object id  For #2
echo '<br>';
echo $tv2;  //tv2 Adj. object id  For  #4
echo '<br>';
echo $contr2; //contr2 Adj. object id  For #5

We can see the output that tv1 and tv2 have different remote controls. This is much more convenient than Scenario 1. Serialization is a recursive process. We don't need to pay attention to how many objects and how many layers of objects are referenced inside the object. We can copy them completely. Note that when using this scheme, we can't trigger the __clone magic method to complete some additional operations. Of course, we can trigger the __clone magic method by performing another clone operation after deep replication, but it will have a small impact on efficiency. In addition, this scenario triggers the __sleep and __wakeup magic methods of the copied object and all referenced objects, so these situations need to be considered.

Summarize

Different object replication methods have different effects. We should consider which method to use and how to improve the replication methods according to the specific application requirements. The object-oriented features of PHP5 are close to those of JAVA. I believe we can learn a lot of valuable experience from JAVA.

For more readers interested in PHP related content, please check the topics on this site: "Introduction to php Object-Oriented Programming", "Encyclopedia of PHP Array (Array) Operation Skills", "Introduction to PHP Basic Syntax", "Summary of PHP Operation and Operator Usage", "Summary of php String (string) Usage", "Introduction to php+mysql Database Operation Skills" and "Summary of php Common Database Operation Skills"

I hope this article is helpful to everyone's PHP programming.


Related articles: