Object Cloning

La Lakshmi Priya Updated 19 Oct 2026
6 min read ·Lesson 143 of 143

What Is Object Cloning?

In simple terms: Cloning creates a copy of an object. Normally, assigning an object to another variable does not copy it — both variables point to the same object. When you need an independent duplicate (for example, to create a new invoice based on an existing one), you clone it.

Technically: The clone operator creates a new object of the same class and copies all properties of the original into it. This is a shallow copy: properties that hold other objects still refer to the same inner objects. If the class defines a __clone() method, PHP calls it on the new object right after copying, so the class can adjust the copy — for example, by cloning nested objects (a deep copy) or resetting identifiers.


Assignment vs Clone

<?php
class Product
{
    public function __construct(public string $name, public float $price) {}
}

$original = new Product('Gel Pen', 20);

$sameObject = $original;
$copy = clone $original;

$sameObject->price = 25;
$copy->price = 30;

echo "Original: {$original->price}\n";
echo "Copy: {$copy->price}\n";
var_dump($original === $sameObject, $original === $copy);

Output

Original: 25
Copy: 30
bool(true)
bool(false)
  • $sameObject = $original copies only the handle; both variables refer to one object, so changing $sameObject changed $original.
  • clone $original creates a separate object; changing the copy does not affect the original.
  • === compares object identity: true only for the same instance.

The Shallow Copy Problem

When an object contains other objects, clone copies the references to them, not the objects themselves:

<?php
class Address
{
    public function __construct(public string $city) {}
}

class Customer
{
    public function __construct(public string $name, public Address $address) {}
}

$asha = new Customer('Asha', new Address('Chennai'));

$bala = clone $asha;
$bala->name = 'Bala';
$bala->address->city = 'Madurai';

echo "{$asha->name} lives in {$asha->address->city}\n";
echo "{$bala->name} lives in {$bala->address->city}\n";

Output

Asha lives in Madurai
Bala lives in Madurai

What Went Wrong

$asha ──► Customer { name: "Asha", address ─┐ }
                                             ├──► Address { city: "Madurai" }
$bala ──► Customer { name: "Bala", address ─┘ }

The name string was copied, but both customers share one Address object. Changing Bala's city changed Asha's too.


Deep Copies With __clone()

__clone() runs on the newly created copy. Inside it, $this is the copy, so nested objects can be cloned as well:

<?php
class Address
{
    public function __construct(public string $city) {}
}

class Customer
{
    public function __construct(public string $name, public Address $address) {}

    public function __clone(): void
    {
        $this->address = clone $this->address;
    }
}

$asha = new Customer('Asha', new Address('Chennai'));

$bala = clone $asha;
$bala->name = 'Bala';
$bala->address->city = 'Madurai';

echo "{$asha->name} lives in {$asha->address->city}\n";
echo "{$bala->name} lives in {$bala->address->city}\n";

Output

Asha lives in Chennai
Bala lives in Madurai

How It Works

  1. clone $asha creates a new Customer with copies of the properties (still sharing the address).
  2. PHP calls __clone() on the new object.
  3. $this->address = clone $this->address; replaces the shared address with its own copy.
  4. Now the two customers are fully independent.

If nested objects contain further objects, each class can define its own __clone(), so deep copying works level by level.


Practical Example: Duplicating an Invoice

A billing system often offers "Duplicate invoice": the new invoice has the same lines but must get a new number, and its lines must be independent of the original.

<?php
final class InvoiceLine
{
    public function __construct(public string $item, public int $quantity, public float $price) {}

    public function total(): float
    {
        return $this->quantity * $this->price;
    }
}

final class Invoice
{
    public ?string $number;
    public array $lines = [];

    public function __construct(string $number, public DateTimeImmutable $issuedAt)
    {
        $this->number = $number;
    }

    public function addLine(InvoiceLine $line): void
    {
        $this->lines[] = $line;
    }

    public function total(): float
    {
        return array_sum(array_map(fn(InvoiceLine $line): float => $line->total(), $this->lines));
    }

    public function __clone(): void
    {
        $this->number = null;   // a duplicate needs a new number
        $this->lines = array_map(fn(InvoiceLine $line): InvoiceLine => clone $line, $this->lines);
    }
}

$original = new Invoice('INV-1001', new DateTimeImmutable('2026-10-15'));
$original->addLine(new InvoiceLine('Notebook', 3, 60));
$original->addLine(new InvoiceLine('Gel Pen', 10, 20));

$duplicate = clone $original;
var_dump($duplicate->number);

$duplicate->number = 'INV-1002';
$duplicate->lines[0]->quantity = 5;

printf("%s: ₹%.2f\n", $original->number, $original->total());
printf("%s: ₹%.2f\n", $duplicate->number, $duplicate->total());

Output

NULL
INV-1001: ₹380.00
INV-1002: ₹500.00

Step-by-Step Explanation

  1. Arrays are copied by value when an object is cloned, but the objects inside the array are still shared. __clone() therefore clones each InvoiceLine with array_map().
  2. __clone() also resets the invoice number, so a duplicate can never accidentally reuse the original's number.
  3. Changing a quantity on the duplicate does not affect the original: ₹380 vs ₹500.
  4. The DateTimeImmutable is not cloned: immutable objects can be shared safely because they never change.

What Needs Deep Copying?

Property TypeCopied by clone?Needs Work in __clone()?
Scalars (int, string, float, bool)Yes, by valueNo
Arrays of scalarsYes, by valueNo
Mutable objects (Address, DateTime, line items)Reference onlyYes: clone them
Arrays containing mutable objectsArray copied, objects sharedYes: clone each element
Immutable objects (DateTimeImmutable, value objects)Reference onlyNo — sharing is safe
Resources (file handles, connections)Same resourceUsually: open a new resource, or prevent cloning

Readonly Properties and Cloning

Readonly properties are copied normally. Since PHP 8.3, they may be re-initialized inside __clone(), which allows deep cloning of readonly objects:

final class Order
{
    public function __construct(public readonly Address $shippingAddress) {}

    public function __clone(): void
    {
        $this->shippingAddress = clone $this->shippingAddress;   // allowed in PHP 8.3+
    }
}

PHP 8.5 adds "clone with", which creates a copy with selected properties changed — very useful for immutable objects:

// PHP 8.5+
$updated = clone($order, ['status' => 'shipped']);

(See Readonly Properties for the "wither" pattern.)


Preventing Cloning

Some objects must never be duplicated — for example, an object representing a database connection or a unique registry. A private __clone() method makes cloning impossible from outside the class:

<?php
final class ConnectionPool
{
    public function __construct() {}

    private function __clone() {}
}

try {
    $copy = clone new ConnectionPool();
} catch (Error $e) {
    echo $e->getMessage() . "\n";
}

Output

Call to private ConnectionPool::__clone() from global scope

Enum cases, closures created by some frameworks, and certain internal objects are also non-cloneable.


Common Mistakes

Expecting Assignment to Copy

Effect: Changes "to the copy" modify the original.

Fix: Use clone when an independent object is needed.

Shallow Copies of Nested Objects

Effect: Original and copy share inner objects and affect each other.

Fix: Clone mutable nested objects in __clone().

Forgetting Arrays of Objects

Effect: The array is copied, but its objects are shared.

Fix: Map over the array and clone each element.

Copying Identity Fields

Effect: Duplicates share IDs, numbers, or tokens that must be unique.

Fix: Reset such fields in __clone().

Cloning Objects That Hold Resources

Effect: Two objects close or use the same file handle or connection.

Fix: Recreate the resource in __clone() or forbid cloning.


Best Practices

  • Use clone deliberately when an independent copy is required.
  • Implement __clone() to deep-copy mutable nested objects and arrays of objects.
  • Reset unique identifiers when duplicating domain objects.
  • Prefer immutable value objects, which can be shared safely and need no deep copy.
  • Make __clone() private for objects that must remain unique.

Official reference: https://www.php.net/manual/en/language.oop5.cloning.php

0 Comments

Reviewed before they appear

No comments yet.

PHP
PHP Home PHP Switch PHP Match Alternative Syntax PHP Loops While Loop Do While Loop For Loop Foreach Loop Nested Loops Break Statement PHP Home PHP Intro PHP Install PHP Built-in Web Server PHP Syntax PHP Tags PHP and HTML PHP Comments PHP Multiline Comments PHP Variables Variables Scope PHP Casting Logical Operators Null Coalescing Operator Spaceship Operator Operator Precedence Continue Statement Modify Strings PHP If...Else...Elseif PHP If Modify Strings PHP If Operators PHP Functions Concatenate Strings PHP If...Else Slicing Strings User-defined Functions Slicing Strings PHP Shorthand if Escape Characters Default Argument Values PHP Nested if Pass by Reference String Interpolation Named Arguments PHP File Upload Variadic Functions Return Values Search in Strings Multibyte Strings Type Declarations in Functions PHP Numbers PHP Math PHP Floats PHP Random Numbers PHP Constants PHP Properties PHP Methods PHP Constructor Constructor Property Promotion PHP Cookies PHP Destructor PHP Access Modifiers Readonly Properties PHP Sessions PHP Inheritance Method Overriding PHP Filters The final Keyword CSRF Protection PHP Filters Advanced Session Security Secure Cookies PHP Magic Constants File Upload Security PHP Callback Functions PHP Operators Filesystem Security PHP JSON Arithmetic Operators Error Reporting in Production Hiding PHP Assignment Operators PHP Configuration (php.ini) Comparison Operators PHP Command Line (CLI) PHP Exceptions Increment / Decrement Operators Migrating to PHP 8 PHP Form Validation PHP Form Required PHP Form URL/E-mail Error Handling Form Input Sanitizing Escaping Output (htmlspecialchars) Custom Error Handler Checkbox, Radio and Select Inputs Sticky Forms PHP Form Complete PHP Date and Time Date Formatting Error Logging HTTP Headers and Redirects Output Buffering Sending Email PHP cURL Consuming REST APIs PHP Generators AJAX PHP AJAX Database AJAX XML AJAX Live Search AJAX Poll AJAX with JSON Security Introduction Password Hashing SQL Injection Prevention XSS Prevention Garbage Collection Project: Simple Blog PHP What is OOP PHP Classes/Objects The $this Keyword Access Array Items Update Array Items Add Array Items Remove Array Items Sorting Arrays Multidimensional Arrays Array Destructuring Array Spread Operator Search in Arrays Merge Arrays PHP Abstract Classes PHP Interfaces Polymorphism PHP Traits PHP Static Methods PHP Static Properties Late Static Binding Magic Methods Object Cloning
Ask about this post
AI Ask about this post

Ask questions about Object Cloning and get answers drawn from it.

Signed-in readers only.