All Projects → myclabs → Deepcopy

myclabs / Deepcopy

Licence: mit
Create deep copies (clones) of your objects

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to Deepcopy

Firebase Instagram
📸 Instagram clone with Firebase Cloud Firestore, Expo, and React Native 😁😍
Stars: ✭ 389 (-95.19%)
Mutual labels:  clone
Jira clone
A Jira clone built with Vuejs & Nodejs/Graphql
Stars: ✭ 666 (-91.77%)
Mutual labels:  clone
Shallow Clone
Make a shallow clone of an object, array or primitive.
Stars: ✭ 23 (-99.72%)
Mutual labels:  clone
Scoold
A Stack Overflow clone for teams (self-hosted)
Stars: ✭ 463 (-94.28%)
Mutual labels:  clone
Pydis
A redis clone in Python 3 to disprove some falsehoods about performance.
Stars: ✭ 623 (-92.3%)
Mutual labels:  clone
Gmail clone
A Gmail Clone built with Flutter
Stars: ✭ 691 (-91.46%)
Mutual labels:  clone
Diablerie
Diablo 2 reimplementation on unity
Stars: ✭ 363 (-95.51%)
Mutual labels:  clone
Medium Clone
🎉 Mediumm WordPress theme, very inspired by medium.com
Stars: ✭ 29 (-99.64%)
Mutual labels:  clone
Tinder React Native
Tinder clone - React Native.
Stars: ✭ 661 (-91.83%)
Mutual labels:  clone
Proxmark3
RRG / Iceman repo, the most totally wicked repo around if you are into Proxmark3
Stars: ✭ 901 (-88.86%)
Mutual labels:  clone
Git Peek
git repo to local editor instantly
Stars: ✭ 485 (-94%)
Mutual labels:  clone
Clone Wars
100+ open-source clones of popular sites like Airbnb, Amazon, Instagram, Netflix, Tiktok, Spotify, Whatsapp, Youtube etc. See source code, demo links, tech stack, github stars.
Stars: ✭ 12,604 (+55.82%)
Mutual labels:  clone
Expo Crossy Road
🐥🚙 Crossy Road game clone made in Expo (iOS, Android, web), THREE.js, Tween, React Native. 🐔
Stars: ✭ 701 (-91.33%)
Mutual labels:  clone
Flutter Instagram Ui Clone
Instagram Ui Clone made using Flutter.
Stars: ✭ 412 (-94.91%)
Mutual labels:  clone
Google Forms
Clone of google forms using React and Node JS.
Stars: ✭ 27 (-99.67%)
Mutual labels:  clone
Ghorg
Quickly clone an entire org/users repositories into one directory - Supports GitHub, GitLab, Bitbucket, and more
Stars: ✭ 377 (-95.34%)
Mutual labels:  clone
Stories
Medium clone built with Ruby on Rails
Stars: ✭ 688 (-91.49%)
Mutual labels:  clone
Expo Stack
🎮🧱 stack game clone made in expo (ios, android, web), three.js, react native
Stars: ✭ 34 (-99.58%)
Mutual labels:  clone
Jetweet
Jetweet is a mini twitter clone with basic functionalities, Made using ASP.NET CORE and Entity framework technologies
Stars: ✭ 29 (-99.64%)
Mutual labels:  clone
Snapchat Clone
👻 A SnapChat clone built with React, Redux and Typescript. Styled with SASS. Tested with Cypress, Jest and Enzyme. Linted with Eslint and formatted with Prettier!
Stars: ✭ 770 (-90.48%)
Mutual labels:  clone

DeepCopy

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

Table of Contents

  1. How
  2. Why
    1. Using simply clone
    2. Overridding __clone()
    3. With DeepCopy
  3. How it works
  4. Going further
    1. Matchers
      1. Property name
      2. Specific property
      3. Type
    2. Filters
      1. SetNullFilter
      2. KeepFilter
      3. DoctrineCollectionFilter
      4. DoctrineEmptyCollectionFilter
      5. DoctrineProxyFilter
      6. ReplaceFilter
      7. ShallowCopyFilter
  5. Edge cases
  6. Contributing
    1. Tests

How?

Install with Composer:

composer require myclabs/deep-copy

Use simply:

use DeepCopy\DeepCopy;

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

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() 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 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

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

  • 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;

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

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

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;

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

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

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)

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

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)

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)

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!

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

$copier = new DeepCopy();
$copier->addFilter(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)

  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)'
  1. 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)

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

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

DeepCopy is distributed under the MIT license.

Tests

Running the tests is simple:

vendor/bin/phpunit

Support

Get professional support via the Tidelift Subscription.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].