From ae684c84fd1be31813faffa78ebd70cdc013fc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9o=20FIDRY?= Date: Tue, 17 Oct 2017 00:02:21 +0200 Subject: [PATCH] Doc tweaks (#89) --- README.md | 176 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 112 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index 308e824..36419a4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,9 @@ DeepCopy helps you create deep copies (clones) of your objects. It is designed to handle cycles in the association graph. -[![Build Status](https://travis-ci.org/myclabs/DeepCopy.png?branch=master)](https://travis-ci.org/myclabs/DeepCopy) [![Coverage Status](https://coveralls.io/repos/myclabs/DeepCopy/badge.png?branch=master)](https://coveralls.io/r/myclabs/DeepCopy?branch=master) [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/myclabs/DeepCopy/badges/quality-score.png?s=2747100c19b275f93a777e3297c6c12d1b68b934)](https://scrutinizer-ci.com/g/myclabs/DeepCopy/) +[![Build Status](https://travis-ci.org/myclabs/DeepCopy.png?branch=master)](https://travis-ci.org/myclabs/DeepCopy) +[![Coverage Status](https://coveralls.io/repos/myclabs/DeepCopy/badge.png?branch=master)](https://coveralls.io/r/myclabs/DeepCopy?branch=master) +[![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/myclabs/DeepCopy/badges/quality-score.png?s=2747100c19b275f93a777e3297c6c12d1b68b934)](https://scrutinizer-ci.com/g/myclabs/DeepCopy/) [![Total Downloads](https://poser.pugx.org/myclabs/deep-copy/downloads.svg)](https://packagist.org/packages/myclabs/deep-copy) @@ -44,8 +46,8 @@ Use simply: ```php use DeepCopy\DeepCopy; -$deepCopy = new DeepCopy(); -$myCopy = $deepCopy->copy($myObject); +$copier = new DeepCopy(); +$myCopy = $copier->copy($myObject); ``` @@ -59,7 +61,8 @@ $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. +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? @@ -85,14 +88,49 @@ Now you're in for a big mess :( ## 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. +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: + +```php +use function DeepCopy\deep_copy; + +$copy = deep_copy($var); +``` + +Alternatively, you can create your own `DeepCopy` instance to configure it differently for example: + +```php +use DeepCopy\DeepCopy; + +$copier = new DeepCopy(true); + +$copy = $copier->copy($var); +``` + +It is however recommended to use a difference instance of `DeepCopy` between each object copy as it relies on +`spl_object_hash` internally. As such you may want to roll your own deep copy function: + +```php +namespace Acme; + +use DeepCopy\DeepCopy; + +function deep_copy($var) +{ + $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->addFilter($filter, $matcher)`, +The method to add a filter is `DeepCopy\DeepCopy::addFilter($filter, $matcher)`, with `$filter` implementing `DeepCopy\Filter\Filter` and `$matcher` implementing `DeepCopy\Matcher\Matcher`. @@ -112,8 +150,8 @@ The `PropertyNameMatcher` will match a property by its name: ```php use DeepCopy\Matcher\PropertyNameMatcher; +// Will apply a filter to any property of any objects named "id" $matcher = new PropertyNameMatcher('id'); -// will apply a filter to any property of any objects named "id" ``` @@ -124,46 +162,49 @@ The `PropertyMatcher` will match a specific property of a specific class: ```php use DeepCopy\Matcher\PropertyMatcher; +// Will apply a filter to the property "id" of any objects of the class "MyClass" $matcher = new PropertyMatcher('MyClass', 'id'); -// will apply a filter to the property "id" of any objects of the class "MyClass" ``` #### 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): +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): ```php 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'); -// will apply a filter to any object that is an instance of 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`. +- `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: +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: ```php use DeepCopy\DeepCopy; use DeepCopy\Filter\SetNullFilter; use DeepCopy\Matcher\PropertyNameMatcher; -$myObject = MyClass::load(123); -echo $myObject->id; // 123 +$object = MyClass::load(123); +echo $object->id; // 123 -$deepCopy = new DeepCopy(); -$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); -$myCopy = $deepCopy->copy($myObject); +$copier = new DeepCopy(); +$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); -echo $myCopy->id; // null +$copy = $copier->copy($object); + +echo $copy->id; // null ``` @@ -176,11 +217,11 @@ 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); +$copier = new DeepCopy(); +$copier->addFilter(new KeepFilter(), new PropertyMatcher('MyClass', 'category')); -// $myCopy->category has not been touched +$copy = $copier->copy($object); +// $copy->category has not been touched ``` @@ -193,26 +234,29 @@ 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); +$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` +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` ```php 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); +$copier = new DeepCopy(); +$copier->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty')); -// $myCopy->myProperty will return an empty collection +$copy = $copier->copy($object); + +// $copy->myProperty will return an empty collection ``` @@ -221,56 +265,60 @@ $myCopy = $deepCopy->copy($myObject); 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!** +**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!** ```php use DeepCopy\DeepCopy; use DeepCopy\Filter\Doctrine\DoctrineProxyFilter; use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; -$deepCopy = new DeepCopy(); -$deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher()); -$myCopy = $deepCopy->copy($myObject); +$copier = new DeepCopy(); +$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher()); + +$copy = $copier->copy($object); -// $myCopy should now contain a clone of all entities, including those that were not yet fully loaded. +// $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: +1. If you want to replace the value of a property: + +```php +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')); - ```php - use DeepCopy\DeepCopy; - use DeepCopy\Filter\ReplaceFilter; - use DeepCopy\Matcher\PropertyMatcher; +$copy = $copier->copy($object); - $deepCopy = new DeepCopy(); - $callback = function ($currentValue) { - return $currentValue . ' (copy)' - }; - $deepCopy->addFilter(new ReplaceFilter($callback), new PropertyMatcher('MyClass', 'title')); - $myCopy = $deepCopy->copy($myObject); +// $copy->title will contain the data returned by the callback, e.g. 'The title (copy)' +``` - // $myCopy->title will contain the data returned by the callback, e.g. 'The title (copy)' - ``` +2. If you want to replace whole element: - 2. If you want to replace whole element: +```php +use DeepCopy\DeepCopy; +use DeepCopy\TypeFilter\ReplaceFilter; +use DeepCopy\TypeMatcher\TypeMatcher; - ```php - 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')); - $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)); +$copy = $copier->copy([new MyClass, 'some string', new MyClass]); - // $myCopy will contain ['MyClass', 'some string', 'MyClass'] - ``` +// $copy will contain ['MyClass', 'some string', 'MyClass'] +``` The `$callback` parameter of the `ReplaceFilter` constructor accepts any PHP callable. @@ -293,7 +341,7 @@ $this->deepCopy->addTypeFilter( ); $myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDependency2::class)); -// all mocks will be just cloned, not deep-copied +// All mocks will be just cloned, not deep copied ``` @@ -307,5 +355,5 @@ DeepCopy is distributed under the MIT license. Running the tests is simple: ```php -phpunit +vendor/bin/phpunit ```