PHPackages                             myclabs/deep-copy - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. myclabs/deep-copy

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

myclabs/deep-copy
=================

Create deep copies (clones) of your objects

1.13.4(9mo ago)8.9k849.8M—9.6%107[17 issues](https://github.com/myclabs/DeepCopy/issues)[3 PRs](https://github.com/myclabs/DeepCopy/pulls)20MITPHPPHP ^7.1 || ^8.0CI passing

Since Oct 1Pushed 4mo ago22 watchersCompare

[ Source](https://github.com/myclabs/DeepCopy)[ Packagist](https://packagist.org/packages/myclabs/deep-copy)[ Fund](https://tidelift.com/funding/github/packagist/myclabs/deep-copy)[ RSS](/packages/myclabs-deep-copy/feed)WikiDiscussions 1.x Synced 1mo ago

READMEChangelog (10)Dependencies (4)Versions (40)Used By (20)

DeepCopy
========

[](#deepcopy)

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

[![Total Downloads](https://camo.githubusercontent.com/e25fe0aa5ee8ac322ea5cf1ed849b27d0cee32ff427ca3e1427433b3958b0edd/68747470733a2f2f706f7365722e707567782e6f72672f6d79636c6162732f646565702d636f70792f646f776e6c6f6164732e737667)](https://packagist.org/packages/myclabs/deep-copy)[![Integrate](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml/badge.svg?branch=1.x)](https://github.com/myclabs/DeepCopy/actions/workflows/ci.yaml)

Table of Contents
-----------------

[](#table-of-contents)

1. [How](#how)
2. [Why](#why)
    1. [Using simply `clone`](#using-simply-clone)
    2. [Overriding `__clone()`](#overriding-__clone)
    3. [With `DeepCopy`](#with-deepcopy)
3. [How it works](#how-it-works)
4. [Going further](#going-further)
    1. [Matchers](#matchers)
        1. [Property name](#property-name)
        2. [Specific property](#specific-property)
        3. [Type](#type)
    2. [Filters](#filters)
        1. [`SetNullFilter`](#setnullfilter-filter)
        2. [`KeepFilter`](#keepfilter-filter)
        3. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
        4. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
        5. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
        6. [`ReplaceFilter`](#replacefilter-type-filter)
        7. [`ShallowCopyFilter`](#shallowcopyfilter-type-filter)
5. [Edge cases](#edge-cases)
6. [Contributing](#contributing)
    1. [Tests](#tests)

How?
----

[](#how)

Install with Composer:

```
composer require myclabs/deep-copy

```

Use it:

```
use DeepCopy\DeepCopy;

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

Why?
----

[](#why)

- 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()`](http://www.php.net/manual/en/language.oop5.cloning.php#object.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](doc/graph.png)](doc/graph.png)

### Using simply `clone`

[](#using-simply-clone)

[![Using clone](doc/clone.png)](doc/clone.png)

### Overriding `__clone()`

[](#overriding-__clone)

[![Overriding __clone](doc/deep-clone.png)](doc/deep-clone.png)

### With `DeepCopy`

[](#with-deepcopy)

[![With DeepCopy](doc/deep-copy.png)](doc/deep-copy.png)

How it works
------------

[](#how-it-works)

DeepCopy recursively traverses all the object's properties and clones them. To avoid cloning the same object twice it keeps a hash map of all instances and thus preserves the object graph.

To use it:

```
use function DeepCopy\deep_copy;

$copy = deep_copy($var);
```

Alternatively, you can create your own `DeepCopy` instance to configure it differently for example:

```
use DeepCopy\DeepCopy;

$copier = new DeepCopy(true);

$copy = $copier->copy($var);
```

You may want to roll your own deep copy function:

```
namespace Acme;

use DeepCopy\DeepCopy;

function deep_copy($var)
{
    static $copier = null;

    if (null === $copier) {
        $copier = new DeepCopy(true);
    }

    return $copier->copy($var);
}
```

Going further
-------------

[](#going-further)

You can add filters to customize the copy process.

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

We provide some generic filters and matchers.

### Matchers

[](#matchers)

- `DeepCopy\Matcher` applies on a object attribute.
- `DeepCopy\TypeMatcher` applies on any element found in graph, including array elements.

#### Property name

[](#property-name)

The `PropertyNameMatcher` will match a property by its name:

```
use DeepCopy\Matcher\PropertyNameMatcher;

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

#### Specific property

[](#specific-property)

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

```
use DeepCopy\Matcher\PropertyMatcher;

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

#### Type

[](#type)

The `TypeMatcher` will match any element by its type (instance of a class or any value that could be parameter of [gettype()](http://php.net/manual/en/function.gettype.php) function):

```
use DeepCopy\TypeMatcher\TypeMatcher;

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

### Filters

[](#filters)

- `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`

By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied). Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.

#### `SetNullFilter` (filter)

[](#setnullfilter-filter)

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;

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

$copier = new DeepCopy();
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));

$copy = $copier->copy($object);

echo $copy->id; // null
```

#### `KeepFilter` (filter)

[](#keepfilter-filter)

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;

$copier = new DeepCopy();
$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category'));

$copy = $copier->copy($object);
// $copy->category has not been touched
```

#### `ChainableFilter` (filter)

[](#chainablefilter-filter)

If you use cloning on proxy classes, you might want to apply two filters for:

1. loading the data
2. applying a transformation

You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e. the next ones may be applied).

```
use DeepCopy\DeepCopy;
use DeepCopy\Filter\ChainableFilter;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Filter\SetNullFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
use DeepCopy\Matcher\PropertyNameMatcher;

$copier = new DeepCopy();
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));

$copy = $copier->copy($object);

echo $copy->id; // null
```

#### `DoctrineCollectionFilter` (filter)

[](#doctrinecollectionfilter-filter)

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;

$copier = new DeepCopy();
$copier->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));

$copy = $copier->copy($object);
```

#### `DoctrineEmptyCollectionFilter` (filter)

[](#doctrineemptycollectionfilter-filter)

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;

$copier = new DeepCopy();
$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));

$copy = $copier->copy($object);

// $copy->myProperty will return an empty collection
```

#### `DoctrineProxyFilter` (filter)

[](#doctrineproxyfilter-filter)

If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a Doctrine proxy class (...\\\_\_CG\_\_\\Proxy). You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class. **Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded before other filters are applied!**We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the cloned lazy loaded entities.

```
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;

$copier = new DeepCopy();
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());

$copy = $copier->copy($object);

// $copy should now contain a clone of all entities, including those that were not yet fully loaded.
```

#### `ReplaceFilter` (type filter)

[](#replacefilter-type-filter)

1. If you want to replace the value of a property:

```
use DeepCopy\DeepCopy;
use DeepCopy\Filter\ReplaceFilter;
use DeepCopy\Matcher\PropertyMatcher;

$copier = new DeepCopy();
$callback = function ($currentValue) {
  return $currentValue . ' (copy)'
};
$copier->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title'));

$copy = $copier->copy($object);

// $copy->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;

$copier = new DeepCopy();
$callback = function (MyClass $myClass) {
  return get_class($myClass);
};
$copier->addTypeFilter(new ReplaceFilter($callback), new TypeMatcher('MyClass'));

$copy = $copier->copy([new MyClass, 'some string', new MyClass]);

// $copy will contain ['MyClass', 'some string', 'MyClass']
```

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

#### `ShallowCopyFilter` (type filter)

[](#shallowcopyfilter-type-filter)

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();
$this->deepCopy->addTypeFilter(
	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
```

Edge cases
----------

[](#edge-cases)

The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are not applied. There is two ways for you to handle them:

- Implement your own `__clone()` method
- Use a filter with a type matcher

Contributing
------------

[](#contributing)

DeepCopy is distributed under the MIT license.

### Tests

[](#tests)

Running the tests is simple:

```
vendor/bin/phpunit
```

### Support

[](#support)

Get professional support via [the Tidelift Subscription](https://tidelift.com/subscription/pkg/packagist-myclabs-deep-copy?utm_source=packagist-myclabs-deep-copy&utm_medium=referral&utm_campaign=readme).

###  Health Score

74

—

ExcellentBetter than 100% of packages

Maintenance66

Regular maintenance activity

Popularity86

Widely adopted with strong download metrics

Community51

Growing community involvement

Maturity80

Battle-tested with a long release history

 Bus Factor2

2 contributors hold 50%+ of commits

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~114 days

Recently: every ~61 days

Total

40

Last Release

139d ago

Major Versions

1.9.4 → 2.x-dev2020-01-09

PHP version history (5 changes)1.0.0PHP &gt;=5.4.0

1.7.0PHP ^5.6 || ^7.0

1.8.0PHP ^7.1

1.9.0PHP ^7.2

1.10.0PHP ^7.1 || ^8.0

### Community

Maintainers

![](https://www.gravatar.com/avatar/329a6111724074f5388e95dd41a03ccf3c43f4bfe1ecf27c94c9efc6f7823228?d=identicon)[mnapoli](/maintainers/mnapoli)

![](https://www.gravatar.com/avatar/8220feb8a3d9f1df987987c33494da696aca6929179a6281cdbe45623fcbec0f?d=identicon)[myclabs](/maintainers/myclabs)

---

Top Contributors

[![mnapoli](https://avatars.githubusercontent.com/u/720328?v=4)](https://github.com/mnapoli "mnapoli (91 commits)")[![theofidry](https://avatars.githubusercontent.com/u/5175937?v=4)](https://github.com/theofidry "theofidry (22 commits)")[![Slamdunk](https://avatars.githubusercontent.com/u/152236?v=4)](https://github.com/Slamdunk "Slamdunk (17 commits)")[![BusterNeece](https://avatars.githubusercontent.com/u/6744885?v=4)](https://github.com/BusterNeece "BusterNeece (6 commits)")[![DavertMik](https://avatars.githubusercontent.com/u/220264?v=4)](https://github.com/DavertMik "DavertMik (6 commits)")[![szepeviktor](https://avatars.githubusercontent.com/u/952007?v=4)](https://github.com/szepeviktor "szepeviktor (5 commits)")[![trebi](https://avatars.githubusercontent.com/u/1706766?v=4)](https://github.com/trebi "trebi (5 commits)")[![llupa](https://avatars.githubusercontent.com/u/41073314?v=4)](https://github.com/llupa "llupa (4 commits)")[![villfa](https://avatars.githubusercontent.com/u/2891564?v=4)](https://github.com/villfa "villfa (3 commits)")[![jdeniau](https://avatars.githubusercontent.com/u/1398469?v=4)](https://github.com/jdeniau "jdeniau (2 commits)")[![fsevestre](https://avatars.githubusercontent.com/u/4130750?v=4)](https://github.com/fsevestre "fsevestre (2 commits)")[![Chris53897](https://avatars.githubusercontent.com/u/7104259?v=4)](https://github.com/Chris53897 "Chris53897 (2 commits)")[![ruudk](https://avatars.githubusercontent.com/u/104180?v=4)](https://github.com/ruudk "ruudk (2 commits)")[![Jan0707](https://avatars.githubusercontent.com/u/1663330?v=4)](https://github.com/Jan0707 "Jan0707 (2 commits)")[![michalananapps](https://avatars.githubusercontent.com/u/132371766?v=4)](https://github.com/michalananapps "michalananapps (2 commits)")[![carusogabriel](https://avatars.githubusercontent.com/u/16328050?v=4)](https://github.com/carusogabriel "carusogabriel (2 commits)")[![VincentLanglet](https://avatars.githubusercontent.com/u/9052536?v=4)](https://github.com/VincentLanglet "VincentLanglet (1 commits)")[![W0rma](https://avatars.githubusercontent.com/u/20659830?v=4)](https://github.com/W0rma "W0rma (1 commits)")[![dritter](https://avatars.githubusercontent.com/u/1544760?v=4)](https://github.com/dritter "dritter (1 commits)")[![youwe-petervanderwal](https://avatars.githubusercontent.com/u/60703382?v=4)](https://github.com/youwe-petervanderwal "youwe-petervanderwal (1 commits)")

---

Tags

cloneclone-deepcloneobjectobject graphduplicatecopy

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/myclabs-deep-copy/health.svg)

```
[![Health](https://phpackages.com/badges/myclabs-deep-copy/health.svg)](https://phpackages.com/packages/myclabs-deep-copy)
```

###  Alternatives

[symfony/property-access

Provides functions to read and write from/to an object or array using a simple string notation

2.8k295.3M2.5k](/packages/symfony-property-access)[symfony/var-exporter

Provides tools to export, instantiate, hydrate, clone and lazy-load PHP objects

2.1k378.1M441](/packages/symfony-var-exporter)[cuyz/valinor

Dependency free PHP library that helps to map any input into a strongly-typed structure.

1.5k9.2M108](/packages/cuyz-valinor)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
