Object Cloning Deep Dive
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 61 of 91.
You learned about the __clone magic method earlier in this course. Now let's explore the critical difference between shallow and deep cloning when objects contain other objects as properties.
By default, PHP performs a shallow copy when cloning. Scalar values are duplicated, but object properties remain as references to the same objects:
<?php
class Address {
public function __construct(public string $city) {}
}
class Person {
public function __construct(
public string $name,
public Address $address
) {}
}
$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->name = "Bob";
$clone->address->city = "London";
echo $original->name . " - " . $original->address->city;
Output:
Alice - LondonNotice that changing $clone->name didn't affect the original, but changing the address city did! Both objects share the same Address instance.
To create a deep copy where nested objects are also cloned, implement __clone:
<?php
class Person {
public function __construct(
public string $name,
public Address $address
) {}
public function __clone(): void {
$this->address = clone $this->address;
}
}
$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";
echo $original->address->city;
Output:
ParisNow each Person has its own independent Address. When your objects contain multiple nested objects, you must clone each one in __clone to achieve true independence between the original and the copy.
Challenge
EasyLet's build a document management system that demonstrates the difference between shallow and deep cloning. You'll create documents that contain author information, and ensure that cloning a document creates a truly independent copy.
You'll organize your code across three files:
Author.php— Create anAuthorclass that represents a document's author. Use constructor promotion to define a public$name(string) and a public$email(string).Document.php— Create aDocumentclass that contains an author. Include the Author file. The class should:- Use constructor promotion for a public
$title(string) and a public$author(Author) - Implement the
__clone()magic method to perform deep cloning — when a document is cloned, its author should also be cloned so changes to the clone's author don't affect the original - Have a method
getInfo()that returns"[title] by [author name] ([author email])"
- Use constructor promotion for a public
main.php— Include the Document file. You'll receive three inputs: a document title, an author name, and an author email.Create a
Documentwith anAuthorusing the provided inputs. Clone the document, then modify the clone's author name by appending" (Copy)"to it and change the clone's title by prepending"Copy of "to it.Print two lines:
- The original document's info
- The cloned document's info
If your deep cloning is implemented correctly, the original document's author should remain unchanged even after modifying the clone's author.
This challenge demonstrates why deep cloning matters — without proper implementation of __clone(), both documents would share the same Author object, and changing one would affect the other.
Cheat sheet
By default, PHP performs a shallow copy when cloning objects. Scalar values are duplicated, but object properties remain as references to the same objects.
Example of shallow cloning:
<?php
class Address {
public function __construct(public string $city) {}
}
class Person {
public function __construct(
public string $name,
public Address $address
) {}
}
$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->name = "Bob";
$clone->address->city = "London";
echo $original->name . " - " . $original->address->city;
// Output: Alice - London
Changing $clone->name doesn't affect the original, but changing the nested address object does because both objects share the same Address instance.
To create a deep copy where nested objects are also cloned, implement the __clone magic method:
<?php
class Person {
public function __construct(
public string $name,
public Address $address
) {}
public function __clone(): void {
$this->address = clone $this->address;
}
}
$original = new Person("Alice", new Address("Paris"));
$clone = clone $original;
$clone->address->city = "London";
echo $original->address->city;
// Output: Paris
When objects contain multiple nested objects, you must clone each one in __clone to achieve true independence between the original and the copy.
Try it yourself
<?php
require_once 'Document.php';
// Read inputs
$title = trim(fgets(STDIN));
$authorName = trim(fgets(STDIN));
$authorEmail = trim(fgets(STDIN));
// TODO: Create an Author with the provided name and email
// TODO: Create a Document with the provided title and the Author
// TODO: Clone the document
// TODO: Modify the clone's author name by appending " (Copy)" to it
// TODO: Modify the clone's title by prepending "Copy of " to it
// TODO: Print the original document's info
// TODO: Print the cloned document's info
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System10Advanced OOP Concepts
Composition vs InheritanceDependency InjectionAnonymous ClassesEnums (PHP 8.1)Fibers (PHP 8.1)Object Cloning Deep DiveGenerators & Iterators2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern