Assigning an object to another variable does not create an independent copy. Both variables refer to the same object instance. clone creates another instance with copied properties, but the copy is shallow: nested object properties can still refer to the same underlying objects. Those distinctions explain why editing a supposed copy sometimes changes the original.
This guide traces assignment and cloning, shows a deliberate __clone hook for one nested value, and compares == with ===. You will also learn why deep copying needs domain decisions rather than an automatic recursive recipe and why resource-owning objects should not always be cloneable.
Shallow copies keep nested object references
Cloning copies the outer object's properties. A scalar such as a string or integer gets its own value in the new object. A nested object property still refers to the same nested instance unless the clone hook or another operation deliberately replaces it with a clone.
Arrays have value semantics, but objects inside those arrays remain shared instances. Therefore an outer clone containing an array of mutable line objects is not automatically a fully independent document. You must decide whether to clone each line, preserve shared immutable values, or create a new document through a controlled copying method.
Resources such as open file handles and database connections are not ordinary independent values that a shallow copy can reproduce safely. Two objects referring to one resource may interfere during cleanup or cursor movement. A resource-owning class can prohibit cloning or define a clear alternative creation operation.
__clone customizes the new object
PHP calls __clone on the new clone after the shallow property copy. The hook can clone a nested mutable value, reset a cache or remove an identity that must not be duplicated. It works on the new object, so $this refers to the clone being prepared.
Avoid copying a database primary key when the new object is meant to represent a new record. A clone is a language-level copy, not automatically a domain-level duplicate operation. A named method such as duplicateAsDraft can make the intended identity, status and ownership changes clearer than a generic clone.
Readonly properties have additional cloning rules that changed in PHP 8.3 and later. Do not assume every clone hook can replace a readonly field on PHP 8.1. The examples here use ordinary mutable properties to teach shallow-copy behavior without newer readonly reinitialization requirements.
Equality and identity answer different questions
For objects, === checks whether both operands refer to the same instance. Two separately constructed labels with equal text are not identical. == compares object class and property values according to PHP's equality rules. That can be useful for a simple demonstration, but it is not always the domain equality you need.
A money value may compare currency and minor units. A user entity may compare stable identity even when display names differ. A document may exclude caches and timestamps from its meaningful equality. Write a dedicated equals method or comparison service when the domain rule differs from a raw property comparison.
Object property comparisons can also involve nested values and recursive structures. Avoid using generic equality as an unexamined assertion over a complex graph. Compare the fields that define the intended contract and make exclusions visible. The PHP object comparison reference and cloning reference document the language behavior.
PHP Object Cloning and Comparison Examples
Each example shows the code, the output it produces and what each part does.
Assignment and clone produce different identity
The clone has independent scalar property storage, so changing its text leaves the original label alone. Shared is still the same instance as original. Identity comparison makes that difference visible without relying on the current text values.
<?php
class Label {
public function __construct(public string $text) {}
}
$original = new Label('Pen');
$shared = $original;
$copy = clone $original;
$copy->text = 'Book';
echo $original->text . ':' . $copy->text . PHP_EOL;
var_export($original === $shared);
echo PHP_EOL;
var_export($original === $copy);
echo PHP_EOL;Pen:Book
true
falseClone one nested mutable object deliberately
The hook makes address independent for this one-level object graph. Removing the hook causes the city change to appear through both profiles. A deeper graph requires additional decisions, especially if objects share children or contain cycles. Do not assume recursively cloning every reachable object preserves those relationships correctly.
<?php
class Address {
public function __construct(public string $city) {}
}
class Profile {
public function __construct(public Address $address) {}
public function __clone() {
$this->address = clone $this->address;
}
}
$original = new Profile(new Address('Lahore'));
$copy = clone $original;
$copy->address->city = 'Karachi';
echo $original->address->city . PHP_EOL;
echo $copy->address->city . PHP_EOL;Lahore
KarachiCommon PHP Object Cloning and Comparison Mistakes
Using serialization as an automatic deep-copy tool
Serializing and unserializing an object graph is not a universal safe copying strategy. Classes can run serialization hooks, resources may not be represented appropriately, and private implementation details can become part of the process. Never unserialize untrusted external data as a way to create a copy.
Prefer a deliberate clone hook for a small graph or a named factory that reconstructs approved data. Preserve shared immutable objects when appropriate, reset identity when duplication creates a new entity, and prohibit cloning where resource ownership cannot be copied meaningfully.
PHP Object Cloning and Comparison in Real Projects
Practical task: compare equal but independent labels
Construct two LabelValue objects with the same text. Compare them with equality and identity, then clone the first and compare identity again. This exercise separates same current data from same object. No mutation is required to prove the difference.
<?php
class LabelValue {
public function __construct(public string $text) {}
}
$first = new LabelValue('Pen');
$second = new LabelValue('Pen');
$copy = clone $first;
var_export($first == $second);
echo PHP_EOL;
var_export($first === $second);
echo PHP_EOL;
var_export($first === $copy);
echo PHP_EOL;true
false
falseThe separate objects compare equal by current class and property values, but they do not share identity. Try changing second's text and explain why equality then becomes false. If these objects represented database users, decide whether equal text should mean equal users; normally a stable user identifier would matter more than a display label.
PHP Object Cloning and Comparison FAQ
Does assigning an object copy it?
No. Another variable refers to the same instance.
Is clone a deep copy?
No. It begins as a shallow property copy. Nested mutable objects need deliberate handling.
Does __clone run on the original?
It runs for the newly cloned object after the initial copy.
Which operator checks the same instance?
Triple equals checks object identity. Double equals compares class and property values using equality rules.
Should I always make objects cloneable?
No. Some represent resources or entities whose duplication needs an explicit domain operation. A clear copying contract matters more than supporting a language shortcut.
PHP Object Cloning and Comparison Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
A cloned profile must have an independent mutable address. What should its copying contract do?
PHP Object Cloning and Comparison Quiz
2 questions to check what you learned.
What does $b = $a do when $a is an object?
- Makes $b refer to the same instance
- Deep-clones every nested object
- Converts the object to an array
Which comparison checks whether two variables refer to the same object instance?
- ==
- ===
- <=