GithubHelp home page GithubHelp logo

deepcopy's Introduction

DeepCopy

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

Total Downloads Integrate

Table of Contents

  1. How
  2. Why
    1. Using simply clone
    2. Overriding __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 it:

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

Overriding __clone()

Overriding __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

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

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

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)

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

  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.

deepcopy's People

Contributors

ayesh avatar bilge avatar busterneece avatar carusogabriel avatar chris8934 avatar davertmik avatar dontub avatar dritter avatar fink3l avatar fridzema avatar fsevestre avatar jan0707 avatar jasny avatar jdeniau avatar jjsaunier avatar jkuchar avatar michaelmoussa avatar mnapoli avatar peter279k avatar remicollet avatar slamdunk avatar systemsolutionweb avatar szepeviktor avatar theofidry avatar thepanz avatar trebi avatar villfa avatar vincentlanglet avatar youwe-petervanderwal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

deepcopy's Issues

Be able to use the original object in cloned properties

Hi, in my example I need to have access to the "source" object because I'd like to link it in one of the cloned properties.

class Foo
{
    /** @var Foo */
    protected $source;
}
// A DeepCopy\Filter\Filter would do this.
$cloner = new DeepCopy();
$foo    = new Foo();
$result = $cloner->copy($foo);

// $result->source == $foo;

The DeepCopy\Filter\Filter::apply($object, $property, $objectCopier) interface should allow the "source" object, therefore DeepCopy::copyObjectProperty aswell.

My first idea was to extend DeepCopy and override some methods, but is too private. I know this feature is not a "cloning" thing but it could be interesting. Is there other way to accomplish??

Thanks!

Mongo BSON objects are not cloneable

Specifically, Class "MongoDB\BSON\ObjectID" is not cloneable. This is a trivial object that simply contains a 24 character hex string. Cloning it is trivial:

$orig = new ObjectID;
$clone = new ObjectID((string) $orig);

I suspect there may be issues with the other BSON types as well, but there's no reason these shouldn't be easily cloneable. It might make more sense to wait for Mongo to get their act together and fix their extension, though.

Stop deepclone with one2many

Hello all,
i'm trying to use your bundle using doctine in Symfony2.
I try to clone an entity named StyleProperty witch has 2 one2many relations and i want it to clone only in one way (styleProperty and all his StyleValues) so I use an KeepFilter not to clone StylePropertyByStyleElement.

 $styleProperty = $em
                    ->getRepository('MyAppMainBundle:StyleProperty')
                    ->findOneById(9);

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
$deepCopy->addFilter(new KeepFilter(), new PropertyMatcher('MyApp\MainBundle\Entity\StyleProperty','stylePropertyByStyleElement'));

$newStyleProperty = $deepCopy->copy($styleProperty);       

$em->persist($newStyleProperty);
$em->flush();

I also tried to set as emptyCollecion :

$deepCopy->addFilter(
new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyApp\MainBundle\Entity\StyleProperty','stylePropertyByStyleElement'));

But it did not work too. :s

It should clone StyleProperty and StyleValue (witch only has an idStyleProperty) but it always wants to clone $stylePropertyByStyleElement and duplicate everything and continue to clone with stylePropertyByStyleElement childs ...

What do I do wrong?

Thank you a lot.

Please add tests in the release tarball

Hey, ๐ŸŽ„
I am packaging DeepCopy on gentoo, to have a complete phpunit, source-only, in our tree,
I noticed that the tests are not in the tarball, maybe I missed something,
I have packaged composer (and all dependencies) and it is not so common for upstreams,
to have tests only in the git tree.

Is there a way that the tests can be in the tarball in the next release ?

PHPUnit 6

For version 2.0 of this package, could we support PHP 7 and use PHPUnit 6?

Mark classes as final

A fair amount of those classes, to not say all, should be final. While it's not doable in 1.x, maybe we could mark them as @final already.

Bump version

Hello,

There is some fix on master :
1.5.0...master

especially this one : #29

Can you bump version please in order to avoid rely on dev-master please ?

thanks

Using DeepCopy from Symfony command

I'm trying to make a copy from a Symfony command.

$deepCopy = new DeepCopy();

$deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));

$myCopy = $deepCopy->copy($product);

But, I got this error:

The identifier id is missing for a query of AppBundle\Entity\Category

It's trying to load the associated entities, but for some reasons the it cannot make the query.

But if I execute the above code from a controller action, it works.

Am I forgetting something?

How to deal with uncloneable objects ?

Hey guys, I am facing a bit of dilemma here. The situation is as follows:

I am currently trying to clone a rather complex tree of documents that I retrieved via doctrine from MongoDB. In order to persist the newly clones documents again, I need to either strip them of their ids or replace new them with new ids (\MongoId to be precise).

My initial approach therefore was to either use replace filter or a setNullFilter like this:

$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));

But then I ran into the exception that DeepCopy still encountered some MongoId Objects that are uncloneable (Which is how I stumbled over the bug I fixed the other day). As it turns out, MongoId Objects are also used in properties that are not called 'id'. Still I thought the fix was rather easy:

$deepCopy->addFilter(new SetNullFilter(), new PropertyTypeMatcher('MongoId'));

After all, this approach should handle all Objects of the MongoId kind, right ? In fact, it does but not in the way I intended it to. It turns out, we use MongoId Objects to reference other objects. These references however should be kept during copying and not be nulled.

Still, I thought the fix was rather easy. I just write my own filter that either duplicates the id manually or keeps it, depending on the property name:


use DeepCopy\Filter\Filter;

class MongoIdFilter implements Filter
{
    /**
     * {@inheritdoc}
     */
    public function apply($object, $property, $objectCopier)
    {
        $reflectionProperty = new \ReflectionProperty($object, $property);
        $reflectionProperty->setAccessible(true);

        if ($property === 'id') {
            $value = new \MongoId();
        } else {
            ldd($reflectionProperty->getValue($object));

            $value = new \MongoId((string) $reflectionProperty->getValue($object));
        }

        $reflectionProperty->setValue($object, $value);
    }
}

Yet, I still run into the exception:

[DeepCopy\Exception\CloneException]
Class "MongoId" is not clone able.

Via debugging I found out that my filter is being executed, but apparently not in every case. I seem to be missing something. Is there any other, preferred way, I should handle this situation ?

Best regards

Jan

Cloning bidirectional relations and keep the same data in cloned entities

I have one doubt lets said that I've entities A, B and C, A hasMany B and B hasMany C but C can have many B also, how should my code looks like? Also I want to clone A but keeping the same information in cloned B and C meaning the only I want to update is the reference to the cloned A, is that what DeepClone does? Any advice? Could you provide an answer explaining this?

Change object class when copying

We should create copy of entity with cnahging it class (doctrine single table inheritance). Currently library doesn't support this.

Use a factory for DeepCopy

Is there a reason not to have a factory to always return the same instance of DeepCopy? Creating a new instance each time comes with an overhead and prevent any use of the internal cashing as nothing is static. For this profile, you can see that it can get quite bad. To be taken with a grain of salt as in the benchmark I was creating a new instance of DeepCopy which has a certain overhead for sure, but from the profiling you can see that most of the time lost comes from the cashing.

Provide an utility function

More an idea than a feature request. In some of my applications I often end up with something like that:

if (false === function_exists('deep_clone')) {
    /**
     * Deep clone the given value.
     *
     * @param mixed $value
     *
     * @return mixed
     */
    function deep_clone($value)
    {
        return (new \DeepCopy\DeepCopy())->copy($value);
    }
}

e.g. in the wild: alice (it's not the only one).

Which actually could be enhanced by being put in a namespace like done in nikic/iter:

namespace MyNamespace;

    /**
     * Deep clone the given value.
     *
     * @param mixed $value
     *
     * @return mixed
     */
    function deep_clone($value)
    {
        return (new \DeepCopy\DeepCopy())->copy($value);
    }
}

I think it could be a nice to have as makes it easier to use (provided you don't have custom config to do).

Document #46

Besides documenting it, I would even recommend it. IMO the biggest selling point of DeepCopy is it's ease of use even on array structures. I don't really like the OOP usage of it and prefer to put it in a function instead like in alice: https://github.com/nelmio/alice/blob/master/src/functions.php#L12

However that's it: I use it because it's convenient. The other selling point of the library is handling cycling graph, but 9/10 a cycling graph is due to a mistake somewhere. Not that there isn't legitimate use cases, but they are very rare.

Copy Entity with all its collections

I have a parent entity named "Sitios" that haves 3 OneToMany associations: "Archivos", "Paginas" and "Hostings"... I am trying to clone the site including the files and pages, and resetting hostings to an empty collection and a "desarrollador" property that needs to be set to null.

this is my code:

$q = $this->entityManager
                ->createQuery('SELECT s,pg,fl FROM IDP:Sitios s
                                JOIN s.paginas pg
                                JOIN s.archivos fl
                                WHERE s.idSitio = :sid')
                ->setParameter('sid', $sid);

$s = $q->getSingleResult();

if ($s == null) {
    throw new EXP\NotFound('invalid_site');
}
$deepCopy = new DeepCopy();
$deepCopy->addFilter(new SetNullFilter(), new PropertyMatcher('Domain\Sitios', 'desarrollador'));
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyMatcher('Domain\Sitios', 'paginas'));
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyMatcher('Domain\Sitios', 'archivos'));
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('Domain\Sitios', 'hostings'));
$sCopy = $deepCopy->copy($s);

$this->entityManager->persist($sCopy);

try {
    $this->entityManager->flush();
} catch (\Exception $e) {
    throw new EXP\InternalError($e->getMessage());
}

But only the Sitio class gets cloned and all the references remains to the original Paginas and Files objects... the desarrollador attribute is not set to null and hostings is alredy empty, so I cant tell if it is working or not...

I have also tried with the collections example

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

but had no luck... can anyone give me a hand please?

Notice: Undefined property: DateTime::$date

Since I updated to PHP 7.0.7 I have an issue duplicating objects with a DateTime property. Every version before 7.0.7 worked without any issues.

the error I am getting is;
Notice: Undefined property: DateTime::$date

The error is thrown at this line:
$propertyValue = $property->getValue($object);

$propertyValue = $property->getValue($object);

there are older bug reports from PHP regarding this issue, so I am unsure if this library should be improved or that this is just PHP related;
http://stackoverflow.com/questions/14084222/why-cant-i-access-datetime-date-in-phps-datetime-class-is-it-a-bug

Also see my try on 3v4l to isolate the issue;
https://3v4l.org/NOGDr

Duplicate entity on persist.

When I save an deepcloned entity, the entity is saved double.

A code snippet

$deepCopy = new DeepCopy();

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

$copy = $deepCopy->copy($item);
//Frefresh the old item
$em->refresh($item);
//Detach the clone
$em->detach($copy);
//Persist the clone
$em->persist($copy);

$em->flush();

Entity User is not clonable

Hi.

Great lib, seems to do just what I need.
Trying it for the first time, and having some trouble.

I have a survey entity fetched from db:

$surveyEntity = $em->getRepository('AppSurveyBundle:Survey')->findOneBy(array('id' => $surveyId, 'deleted' => null));

This is the entity I want to clone, so;

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

Trying to run this, I get an exception telling me that "'App\UserBundle\Entity\User' is not cloneable.".
This entity is not referenced anywhere in my entity as far as I can tell. Searched through the entire bundle for any signs of "user", but there are none, as expected. My user entity has nothing to do with the survey entity.

Any pointers for me debugging this?
What makes an entity un-clonable?

General error: property queryString is read only

Since we upgraded to the latest version, 1.4.0 we are getting this error when running tests in codeception.

  • Removing myclabs/deep-copy (1.3.1)
  • Installing myclabs/deep-copy (1.4.0)
PDOException: SQLSTATE[HY000]: General error: property queryString is read only

#1  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:160
#2  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:123
#3  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:75
#4  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:160
#5  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:123
#6  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:75
#7  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:86
#8  DeepCopy\DeepCopy->DeepCopy\{closure}
#9  /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:89
#10 /var/jenkins/workspace/Integration-API-MAIN-PHP-Testing/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php:68

New Release?

Hi
Any chance of a new release please? A couple of fixes have made their way in now which I'm reliant on and I'd prefer not to be running from dev-master if at all possible!

Thanks!

I've got redeclared function deep_copy

I've got this error while do phpunit.. PHP Fatal error: Cannot redeclare DeepCopy\deep_copy() (previously declared in /usr/share/php/DeepCopy/deep_copy.php:13)
Maybe add check function_exists before declare function should be a solution.

KeepFilter and PropertyTypeMatcher

It is currently possible to use the KeepFilter with the PropertyTypeMatcher (no exception thrown) but it's not working:

$deepCopy = new DeepCopy();
...
$deepCopy->addFilter(new KeepFilter(), new PropertyTypeMatcher('AppBundle\Entity\Group'));
...
$versionCopy = $deepCopy->skipUncloneable()->copy($version);

I have this error:

Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship 'AppBundle\Entity\Version#groups' that was not configured to cascade persist operations for entity: AppBundle\Entity\Group@000000003121925c000000001779ad23. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist  this association in the mapping for example @ManyToOne(..,cascade={"persist"}). If you cannot find out which entity causes the problem implement 'AppBundle\Entity\Group#__toString()' to get a clue.

The filters property is iterate after cloning the object (https://github.com/myclabs/DeepCopy/blob/master/src/DeepCopy/DeepCopy.php#L137), but should be checked before the clone (https://github.com/myclabs/DeepCopy/blob/master/src/DeepCopy/DeepCopy.php#L119).

Skip dynamic properties

Add a mode where dynamic properties can be skipped: I expect this could significantly improve performances and be good enough for some users.

Bug in \DeepCopy\DeepCopy::copyObject method

Hi.
next lines in code are buggy, that our project failing today:

$objectHash = spl_object_hash($object);
..........
..........
$newObject = clone $object;
//One of two situations (I do not know correct solution)
//1)
$this->hashMap[spl_object_hash($newObject)] = $newObject; //<--- BUG: need use spl_object_hash($newObject) instead previous $objectHash

//2)
$this->hashMap[$objectHash] = $object;//<---- store old object

What is happening: The current $object will free (as cloned from previous recursions). The php reusing this $objectHash for new cloned $object. But in this case, the copy process returning old not correct $object from $this->hashMap.

PLEASE fix it and inform me (I don't want to inject my codes inside ).

Make it faster

Last time I looked, I couldn't find any way to really squeeze much more perf. This is mainly due to the fact that DeepCopy deep copy an object. As such, you cannot assume the properties of that object as values can be dynamically added. The result is DeepCopy must rely on a brand new instance of ReflectionObject each time, even if you get two objects of the same class.

A way to change that would maybe to add a mode to clone class instances instead, i.e. to rely on ReflectionClass and cache it somehow.

But to be honest this is just an idea like that. I'm actually not sure if it's really worth it, maybe it would be better to start by having a benchmark and see if the perf gain is worth it.

Change Matcher API

Right now an matcher gets a string as a second argument, cf. this line. IMO this is quite inefficient for two reasons:

  • First ReflectionProperty is much more powerful and easy to manipulate than just a name. For example in PropertyTypeMatcher we end up doing reflection again... when it could be just passed down.
  • It is actually wrong. If you take the example above (PropertyTypeMatcher), the property could come from a parent class and as a result the reflection will throw an exception. While if you pass the ReflectionProperty, you actually get the right one.

DroctrineEmptyCollectionFilter does not work with Symfony2

Hello,

I have a Symfony2 project with Doctrine2 and I try to use DroctrineEmptyCollectionFilter so as to reset a array collection property while the deepCopy but it does not seem to work. The property keeps his array with all the data from the copied object :

$styleProperty = $em
->getRepository('MyAppMainBundle:StyleProperty')
->findOneById(9);

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

$deepCopy->addFilter(
new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyApp\MainBundle\Entity\StyleProperty','stylePropertyByStyleElement'));

$newStyleProperty = $deepCopy->copy($styleProperty);

Do I do something wrong?

Matches are expected to access private properties in a derived class

I'd like to start off by saying I am very surprised a basic inheritance issue such as this still exists in software that has been in development for four years.

As demonstrated in the following example, the filter's matcher (i.e. PropertyTypeMatcher) is expected to access a private property of the parent class in a derived class. Needless to say, it fails, because the property does not exist in the derived class.

class A {
    private $foo;

    public function __construct($foo) {
        $this->foo = $foo;
    }
}

class B extends A {}

$copier = new DeepCopy;
$copier->addFilter(new KeepFilter, new PropertyTypeMatcher(stdClass::class));
$copier->copy(new B((object)[]);

Property B::$foo does not exist
src\DeepCopy\Matcher\PropertyTypeMatcher.php:33

Implement Matcher and Filter that processes also array members.

For now, filter applies only on property of an object. But for filter PropertyByTypeFilter, it would be more consistent, if its applies on all existing instances of specified type that appears inside copied elemnt, so not just object properties, but also members of arrays, or copied object itself (i.e. $deepCopy->clone(\Mockery::mock(MySuperClass::class)).

This is particullary useful to configure DeepCopy to skip all mocked instances (I'm using Mockery), which oviously, DeepCopy fails to clone.

Can DeepCopy also copy recursions in an object?

Here are some examples:

$obj = new stdClass();
$obj->foo = $foo;

or more complex:

$foo = new stdClass();
$bar = new stdClass();
$foo->bar = $bar;
$bar->foo = $foo;

Are these cases supported?

After DoctrineProxyFilter it is not possible to apply further filters

As @IMM0rtalis mentioned it in his comment #18 (comment) there is in my opinion an undesired behavior with following code:

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

The DeepCopy-Class loads the proxy, but do not apply further filters like to set null the id. Therefore, Doctrine can not handle the copied entity as a new entity.

Do I see it correclty?

Best,
Christoph

Doctrine 2 persisting new objects

I know your project is used more braodly. But I am using Doctrine 2 and trying to copy entites, which are now generated, but cannot be written to the database because they are not persistet. I can and will not set cascading persists in our project, so I was wondering if I can use the filter mechanism to persist entities upon creation?

Move to an immutable API

I'm not really sure what's the interest of having an addFilter and co. when everything could be added in the constructor.

Class "ReflectionClass" is not cloneable

Hi,

i'm getting this error when i follow README instructions for installing.

Class "ReflectionClass" is not cloneable.

This is my code (my "duplicate" controller):

$em = $this->getDoctrine()->getEntityManager();
$entity = $em->getRepository("AppBundle:MyEntity")->find($id); 
$deepCopy = new DeepCopy(); 
$clone = $deepCopy->copy($entity); 
$clone->setName("Copy of " . $clone->getName()); 
$em->persist($clone); 
$em->flush();

Thanks!

Doctrine Bidirectional Relationship Copying

Hi

I have two entities with a BIDirectional Relationship, Timeslices and Activities.
Timeslice has an Ativity and Activity has multiple Timeslices.
When i Copy a Timeslice i want to keep the Reference to Activity. I Use a KeepFilter for that which Keeps the Activity but does not update the Timeslices Field in Activity. Doctrine thus looses the Relationship.

If I remove the KeepFilter It tries to make a new Activity which isnt desired either.
Have you encountered this Situation before?

DeepCopy ignores Doctrine2 proxies

If the relation is fully hydrated (ie, no proxy) everything is good, and my "id" property matcher can set the property to null.

If, however, the relation isn't retrieved in the query, the related object is Proxy instance, and DeepCopy won't set the ID of that object to null.

Has anyone had this problem, and has anyone got a workaround, other than hydrating everything before copying (which might be the most sensible thing to do anyway)?

DoctrineEmptyCollectionFilter and DoctrineProxyMatcher are not used and have errors

DoctrineEmptyCollectionFilter does not appear to be used in this repo, but it is there and also references "Doctrine\Common\Collections\ArrayCollection" which is not included.

Similarly, the class "Proxy" (Doctrine\Common\Persistence\Proxy) also does not exist is referenced by DoctrineProxyMatcher.

This does not affect the project's runtime, but I found this result through a kind of static analysis tool.

can't clone with hhvm

the same code is working with php-fpm and fails on hhvm.

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

Uncaught PHP Exception BadMethodCallException: "Trying to clone an uncloneable object of class ReflectionClass" at line {"exception":"[object](BadMethodCallException%28code: 0%29: Trying to clone an uncloneable object of class ReflectionClass at :)

hhvm --version
HipHop VM 3.5.0-dev (rel)
Compiler: heads/master-0-gcb697d9b910189e25fbddce39e7672dfd27d4f04
Repo schema: 8b97230efbcbe284b1e86ce2768b04e5145b36f2
Extension API: 20140829

Maximum function nesting level of '200' reached, aborting

Hi,
I've updated to 1.4.0 and my tests failed with a Maximum function nesting level of '200' reached, aborting exception.

Rolled back to 1.3.1 everything went back to normal.

I don't know if I missed something (config or ... something) but I thought you might be notified.

thanks

composer update error

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating optimized autoload files

Illuminate\Foundation\ComposerScripts::postUpdate
PHP Warning: Uncaught ErrorException: require(C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer/../myclabs/deep-copy/src/DeepCopy/deep_copy.php): failed to open stream: No such file or directory in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php:66
Stack trace:
#0 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(66): Composer\Util\ErrorHandler::handle(2, 'require(C:\User...', 'C:\Users\Gary-C...', 66, Array)
#1 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(66): require()
#2 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(56): composerRequirec1eacf11190380a1fd3b9f0e78877bfb('6124b4c8570aa39...', 'C:\Users\Gary-C...')
#3 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\autoload.php(7): ComposerAutoloaderInitc1eacf11190380a1fd3b9f0e78877bfb::getLoader()
#4 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\laravel\framework\src in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php on line 66

Warning: Uncaught ErrorException: require(C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer/../myclabs/deep-copy/src/DeepCopy/deep_copy.php): failed to open stream: No such file or directory in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php:66
Stack trace:
#0 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(66): Composer\Util\ErrorHandler::handle(2, 'require(C:\User...', 'C:\Users\Gary-C...', 66, Array)
#1 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(66): require()
#2 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php(56): composerRequirec1eacf11190380a1fd3b9f0e78877bfb('6124b4c8570aa39...', 'C:\Users\Gary-C...')
#3 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\autoload.php(7): ComposerAutoloaderInitc1eacf11190380a1fd3b9f0e78877bfb::getLoader()
#4 C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\laravel\framework\src in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php on line 66
PHP Fatal error: composerRequirec1eacf11190380a1fd3b9f0e78877bfb(): Failed opening required 'C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer/../myclabs/deep-copy/src/DeepCopy/deep_copy.php' (include_path='C:\xampp\php\PEAR') in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php on line 66

Fatal error: composerRequirec1eacf11190380a1fd3b9f0e78877bfb(): Failed opening required 'C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer/../myclabs/deep-copy/src/DeepCopy/deep_copy.php' (include_path='C:\xampp\php\PEAR') in C:\Users\Gary-Carvajal\Documents\GitHub\blsempl_unah\vendor\composer\autoload_real.php on line 66

When updating the composer gives me this error if someone has the solution please could you help me......

Create branch 2.0

In several issues i read a lot of nice ideas how to improve this nice project. But like already suggested from others I would like to have a branch 2.0 or sth like this. Can you create this pls?

"FOS\UserBundle\Model\User" is not cloneable

Doing the following still tries to copy the User object - how do I stop this cloning happening?

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('user_id'));
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('user'));
$clone = $deepCopy->copy($entity);

DoctrineCollectionFilter does not work if a remover is present

If you have an entity like this:

class thing {
  /** @var ArrayCollection **/
  private $blas;

  public function setBlas($blas) {
    // setting logic
  }
  public function addBla($bla) {
    // adding logic
  }
  public function removeBla($bla) {
  // removing logic
  }
}

If you implement the DoctrineCollectionFilter to copy the vales stored in $this->blas, the code uses Symfony's PropertyAccessor class that prioritises the use of remove/add over set. Thus it will attempt to alter the contents of the ArrayCollection, but the ArrayCollection itself will be the reference to the original one, and not a copy. If you remove the removeBla() method from the example above, it will use setBlas that can be used to create a new ArrayCollection and therefore not use the reference to the original.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.