Please wait

Cloning Objects

Cloning an object in PHP means creating a copy of that object. When you clone an object, you produce a new instance of the same class, with its properties set to the values of the original object.

Copying with the Assignment Operator

Let's say we had the following class:

class Book {
  public function __construct(public $title, public $author) {
 
  }
 
  public function getDetails() {
    return "Title: $this->title, Author: $this->author";
  }
}
 
// Creating an original book object
$original = new Book("1984", "George Orwell");
 
// "Cloning" the original book object using the assignment operator
$copied = $original;
 
// Changing title in copied object
$copied->title = "Animal Farm";
 
// Output details of both objects
echo $original->getDetails();  // Outputs: Title: Animal Farm, Author: George Orwell
echo "\n";
echo $copied->getDetails();   // Outputs: Title: Animal Farm, Author: George Orwell

When you run the code, you'll notice that both $original and $copied now have the title "Animal Farm", even though we only changed it in the $copied object.

When you use the assignment operator to "clone" an object, you are not actually creating a new instance. Instead, you're simply copying the reference of the original object. This means that both variables ($original and $copied in this case) point to the same object in memory. Any changes you make to one variable will reflect in the other.

This can lead to unintended side effects and can be particularly problematic when dealing with more complex objects that hold state, have methods that modify properties or manage resources.

The clone Keyword

Using the assignment operator is not an effective way to copy objects if you want to have unique copies. For this reason, the clone operator was introduced to help developers create unique copies of objects.

Here's the basic syntax for using this keyword.

$copy = clone $original;

Let's look at how it can be helpful with cloning the Book class.

// Creating an original book object
$original = new Book("1984", "George Orwell");
 
// Cloning the original book object
$copied = clone $original;
 
// Changing title in copied object
$copied->title = "Animal Farm";
 
// Output details of both objects
echo $original->getDetails();  // Outputs: Title: 1984, Author: George Orwell
echo "\n";
echo $copied->getDetails();   // Outputs: Title: Animal Farm, Author: George Orwell

In this example, we first created an original Book object with the title "1984". Then, we cloned that object and changed the title in the cloned object to "Animal Farm". Finally, we output the details of both objects, demonstrating that they're two distinct instances; changing the title in the copied object didn't affect the original object.

Shallow Cloning

By default, PHP performs a shallow copy, meaning that if the object contains references to other objects, the references are copied, not the actual objects they point to. As a result, the cloned object and the original will still reference the same objects.

Imagine you have a box (an object) with a toy (another object) inside. When you perform a shallow clone of the box, you create a new box, but both boxes still contain the same toy. If you make changes to the toy inside one box, the toy in the other box will show the same changes since they are the same toy.

In technical terms, when you clone an object that has references to other objects, a shallow clone will copy the references, not the actual referenced objects. This means the cloned object and the original object will still point to the same inner objects.

Using the above analogy, if you wanted the second box to have its own separate toy, shallow cloning would be a problem because any change to the toy (like painting it a different color) would affect the toy in both boxes since they're the same toy.

Let's illustrate with a simple example using PHP classes:

class Toy {
  public $color;
}
 
class Box {
  public $toy;
 
  public function __construct($color) {
    $this->toy = new Toy();
    $this->toy->color = $color;
  }
}
 
// Original box with a red toy
$originalBox = new Box("red");
 
// Shallow clone of the box
$clonedBox = clone $originalBox;
 
// Paint the toy in the cloned box to blue
$clonedBox->toy->color = "blue";
 
// Check the color of toys in both boxes
echo $originalBox->toy->color;  // Outputs: blue
echo $clonedBox->toy->color;   // Outputs: blue

In this example, even though we changed the toy color in the $clonedBox to blue, the toy color in $originalBox also changed to blue. This is because the shallow clone caused both $originalBox and $clonedBox to share the same Toy object.

Deep Cloning

We can resolve this issue with deep cloning. Deep cloning refers to creating a copy of an object along with copies of all objects that are referenced by the original. This means that any object inside our main object will also be cloned, ensuring that the new object is completely independent of the original.

In contrast, shallow cloning only creates a copy of the main object, but references inside this object (like pointers to other objects) remain intact. In other words, the cloned object and the original object will still point to the same inner objects.

In the context of programming, especially when dealing with complex objects with nested structures, deep cloning is often preferred when you want to ensure that modifications in one object do not inadvertently affect another object. However, deep cloning can be more resource-intensive, as it requires creating entirely new instances for every nested object.

To perform deep cloning, we can use the __clone() magic method. The __clone() magic method in PHP is a special method that gets triggered when an object is cloned using the clone keyword. It's primarily meant to give developers a chance to perform additional operations during the cloning process, like ensuring deep copies of nested objects.

By defining the __clone() method, we can ensure a deep copy of nested objects and prevent the issues that arise with shallow cloning.

Let's go back to our Box and Toy example to see how this can work:

class Toy {
  public $color;
}
 
class Box {
  public $toy;
 
  public function __construct($color) {
    $this->toy = new Toy();
    $this->toy->color = $color;
  }
 
  public function __clone() {
    // Create a deep copy of the Toy object
    $this->toy = clone $this->toy;
  }
}
 
// Original box with a red toy
$originalBox = new Box("red");
 
// Clone the box (which triggers __clone)
$clonedBox = clone $originalBox;
 
// Paint the toy in the cloned box to blue
$clonedBox->toy->color = "blue";
 
// Check the color of toys in both boxes
echo $originalBox->toy->color;  // Outputs: red
echo $clonedBox->toy->color;   // Outputs: blue

By defining the __clone() method in the Box class, we ensured that when a Box object is cloned, its Toy object is also cloned (deep copy). This prevents the problem we observed with shallow cloning, as now both the $originalBox and $clonedBox have their own distinct Toy objects.

Key Takeaways

  • Cloning creates a copy of an object. In PHP, you typically use the clone keyword to clone objects.
  • Shallow cloning creates a copy of the main object. References to inner objects remain intact. Both the original and cloned objects point to the same inner objects. Changes to inner objects in one will affect the other.
  • Deep cloning creates a copy of the main object and all nested objects.
  • More resource-intensive than shallow cloning.
  • The __clone() magic method is automatically triggered when an object is cloned using the clone keyword. It can be used to implement deep cloning and overcome issues of shallow cloning.
  • Using the assignment operator (=) to "copy" objects only duplicates the reference, not the object itself. Both variables point to the same object in memory.

Comments

Please read this before commenting