Create deep copies (clones) of your objects

Installs: 432 440

Dependents: 8

Stars: 101

Watchers: 12

Forks: 16

Open Issues: 1

1.5.0 2015-11-07 22:20 UTC


DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph.

Build Status Coverage Status Scrutinizer Quality Score Total Downloads


Install with Composer:

composer require myclabs/deep-copy

Use simply:

use DeepCopy\DeepCopy;

$deepCopy = new DeepCopy();
$myCopy = $deepCopy->copy($myObject);


  • How do you create copies of your objects?
$myCopy = clone $myObject;
  • How do you create deep copies of your objects (i.e. copying also all the objects referenced in the properties)?

You use __clone() and implement the behavior yourself.

  • But how do you handle cycles in the association graph?

Now you're in for a big mess :(

association graph

Using simply clone

Using clone

Overridding __clone()

Overridding __clone

With DeepCopy

With DeepCopy

How it works

DeepCopy traverses recursively all your object's properties and clones them.

To avoid cloning the same object twice (and thus, keep you object graph), it keeps a hash-map of all instances.

Going further

You can add filters to customize the copy process.

The method to add a filter is $deepCopy->addFilter($filter, $matcher), with $filter implementing DeepCopy\Filter\Filter and $matcher implementing DeepCopy\Matcher\Matcher.

We provide some generic filters and matchers.


  • DeepCopy\Matcher applies on a object attribute.
  • DeepCopy\TypeMatcher applies on any element found in graph, including array elements.
Property name

The PropertyNameMatcher will match a property by its name:

use DeepCopy\Matcher\PropertyNameMatcher;

$matcher = new PropertyNameMatcher('id');
// will apply a filter to any property of any objects named "id"
Specific property

The PropertyMatcher will match a specific property of a specific class:

use DeepCopy\Matcher\PropertyMatcher;

$matcher = new PropertyMatcher('MyClass', 'id');
// will apply a filter to the property "id" of any objects of the class "MyClass"

The TypeMatcher will match any element by its type (instance of a class or any value that could be parameter of gettype() function):

use DeepCopy\TypeMatcher\TypeMatcher;

$matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
// will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection


  • DeepCopy\Filter applies a transformation to the object attribute matched by DeepCopy\Matcher.
  • DeepCopy\TypeFilter applies a transformation to any element matched by DeepCopy\TypeMatcher.

Let's say for example that you are copying a database record (or a Doctrine entity), so you want the copy not to have any ID:

use DeepCopy\DeepCopy;
use DeepCopy\Filter\SetNullFilter;
use DeepCopy\Matcher\PropertyNameMatcher;

$myObject = MyClass::load(123);
echo $myObject->id; // 123

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
$myCopy = $deepCopy->copy($myObject);

echo $myCopy->id; // null

If you want a property to remain untouched (for example, an association to an object):

use DeepCopy\DeepCopy;
use DeepCopy\Filter\KeepFilter;
use DeepCopy\Matcher\PropertyMatcher;

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));
$myCopy = $deepCopy->copy($myObject);

// $myCopy->category has not been touched
  1. If you want to replace the value of a property:

    use DeepCopy\DeepCopy;
    use DeepCopy\Filter\ReplaceFilter;
    use DeepCopy\Matcher\PropertyMatcher;
    $deepCopy = new DeepCopy();
    $callback = function ($currentValue) {
        return $currentValue . ' (copy)'
    $deepCopy->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));
    $myCopy = $deepCopy->copy($myObject);
    // $myCopy->title will contain the data returned by the callback, e.g. 'The title (copy)'
  2. If you want to replace whole element:

    use DeepCopy\DeepCopy;
    use DeepCopy\TypeFilter\ReplaceFilter;
    use DeepCopy\TypeMatcher\TypeMatcher;
    $deepCopy = new DeepCopy();
    $callback = function (MyClass $myClass) {
        return get_class($myClass);
    $deepCopy->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));
    $myCopy = $deepCopy->copy(array(new MyClass, 'some string', new MyClass));
    // $myCopy will contain ['MyClass', 'some stirng', 'MyClass']

The $callback parameter of the ReplaceFilter constructor accepts any PHP callable.


Stop DeepCopy from recursively copying element, using standard clone instead:

use DeepCopy\DeepCopy;
use DeepCopy\TypeFilter\ShallowCopyFilter;
use DeepCopy\TypeMatcher\TypeMatcher;
use Mockery as m;

$this->deepCopy = new DeepCopy();
    new ShallowCopyFilter,
    new TypeMatcher(m\MockInterface::class)

$myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class));
// all mocks will be just cloned, not deep-copied 

If you use Doctrine and want to copy an entity, you will need to use the DoctrineCollectionFilter:

use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
use DeepCopy\Matcher\PropertyTypeMatcher;

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
$myCopy = $deepCopy->copy($myObject);

If you use Doctrine and want to copy an entity who contains a Collection that you want to be reset, you can use the DoctrineEmptyCollectionFilter

use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
use DeepCopy\Matcher\PropertyMatcher;

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
$myCopy = $deepCopy->copy($myObject);

// $myCopy->myProperty will return an empty collection


DeepCopy is distributed under the MIT license.


Running the tests is simple: