Skip to content

Commit 7a84e10

Browse files
authored
Rework tests (#85)
I've did a review of the lib and did some cleanup, tweaks and added missing tests. - Bumped minimal version to PHP 5.6. - Fixed the PHPUnit config to exclude `deep_copy.php` from the coverage report - Fixed a bug when using `useCloneMethod`: the old object was returned unchanged instead of returning the cloned object - Make the coverage stricter by adding the `@covers` annotations - Add missing tests - Cosmetic cleanup
1 parent 66a912a commit 7a84e10

39 files changed

+981
-606
lines changed

.travis.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ language: php
33
sudo: false
44

55
php:
6-
- '5.5'
76
- '5.6'
87
- '7.0'
98
- '7.1'
@@ -12,7 +11,7 @@ php:
1211
matrix:
1312
fast_finish: true
1413
include:
15-
- php: '5.4'
14+
- php: '5.6'
1615
env: COMPOSER_FLAGS="--prefer-lowest"
1716
allow_failures:
1817
- php: nightly
@@ -23,7 +22,7 @@ cache:
2322

2423
before_install:
2524
- |
26-
if [ "$TRAVIS_PHP_VERSION" = "nightly" ] || "$TRAVIS_PHP_VERSION" = "7.1" ]; then
25+
if [ "$TRAVIS_PHP_VERSION" = "nightly" ]; then
2726
COMPOSER_FLAGS="$COMPOSER_FLAGS --ignore-platform-reqs"
2827
fi;
2928

README.md

Lines changed: 79 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,17 @@ DeepCopy helps you create deep copies (clones) of your objects. It is designed t
2020
1. [Specific property](#specific-property)
2121
1. [Type](#type)
2222
1. [Filters](#filters)
23-
1. [`SetNullFilter`](#setnullfilter)
24-
1. [`KeepFilter`](#keepfilter)
25-
1. [`ReplaceFilter`](#replacefilter)
26-
1. [`ShallowCopyFilter`](#doctrinecollectionfilter)
27-
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter)
28-
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter)
23+
1. [`SetNullFilter`](#setnullfilter-filter)
24+
1. [`KeepFilter`](#keepfilter-filter)
25+
1. [`DoctrineCollectionFilter`](#doctrinecollectionfilter-filter)
26+
1. [`DoctrineEmptyCollectionFilter`](#doctrineemptycollectionfilter-filter)
27+
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
28+
1. [`ReplaceFilter`](#replacefilter-type-filter)
29+
1. [`ShallowCopyFilter`](#doctrinecollectionfilter-type-filter)
2930
1. [Contributing](#contributing)
3031
1. [Tests](#tests)
3132

33+
3234
## How?
3335

3436
Install with Composer:
@@ -65,22 +67,27 @@ Now you're in for a big mess :(
6567

6668
![association graph](doc/graph.png)
6769

70+
6871
### Using simply `clone`
6972

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

75+
7276
### Overridding `__clone()`
7377

7478
![Overridding __clone](doc/deep-clone.png)
7579

80+
7681
### With `DeepCopy`
7782

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

85+
8086
## How it works
8187

8288
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.
8389

90+
8491
## Going further
8592

8693
You can add filters to customize the copy process.
@@ -91,11 +98,13 @@ and `$matcher` implementing `DeepCopy\Matcher\Matcher`.
9198

9299
We provide some generic filters and matchers.
93100

101+
94102
### Matchers
95103

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

107+
99108
#### Property name
100109

101110
The `PropertyNameMatcher` will match a property by its name:
@@ -107,6 +116,7 @@ $matcher = new PropertyNameMatcher('id');
107116
// will apply a filter to any property of any objects named "id"
108117
```
109118

119+
110120
#### Specific property
111121

112122
The `PropertyMatcher` will match a specific property of a specific class:
@@ -118,6 +128,7 @@ $matcher = new PropertyMatcher('MyClass', 'id');
118128
// will apply a filter to the property "id" of any objects of the class "MyClass"
119129
```
120130

131+
121132
#### Type
122133

123134
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):
@@ -129,12 +140,14 @@ $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
129140
// will apply a filter to any object that is an instance of Doctrine\Common\Collections\Collection
130141
```
131142

143+
132144
### Filters
133145

134146
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`.
135147
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`.
136148

137-
#### `SetNullFilter`
149+
150+
#### `SetNullFilter` (filter)
138151

139152
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:
140153

@@ -153,7 +166,8 @@ $myCopy = $deepCopy->copy($myObject);
153166
echo $myCopy->id; // null
154167
```
155168

156-
#### `KeepFilter`
169+
170+
#### `KeepFilter` (filter)
157171

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

@@ -169,7 +183,60 @@ $myCopy = $deepCopy->copy($myObject);
169183
// $myCopy->category has not been touched
170184
```
171185

172-
#### `ReplaceFilter`
186+
187+
#### `DoctrineCollectionFilter` (filter)
188+
189+
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
190+
191+
```php
192+
use DeepCopy\DeepCopy;
193+
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
194+
use DeepCopy\Matcher\PropertyTypeMatcher;
195+
196+
$deepCopy = new DeepCopy();
197+
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
198+
$myCopy = $deepCopy->copy($myObject);
199+
```
200+
201+
202+
#### `DoctrineEmptyCollectionFilter` (filter)
203+
204+
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`
205+
206+
```php
207+
use DeepCopy\DeepCopy;
208+
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
209+
use DeepCopy\Matcher\PropertyMatcher;
210+
211+
$deepCopy = new DeepCopy();
212+
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
213+
$myCopy = $deepCopy->copy($myObject);
214+
215+
// $myCopy->myProperty will return an empty collection
216+
```
217+
218+
219+
#### `DoctrineProxyFilter` (filter)
220+
221+
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
222+
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
223+
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
224+
**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!**
225+
226+
```php
227+
use DeepCopy\DeepCopy;
228+
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
229+
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
230+
231+
$deepCopy = new DeepCopy();
232+
$deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
233+
$myCopy = $deepCopy->copy($myObject);
234+
235+
// $myCopy should now contain a clone of all entities, including those that were not yet fully loaded.
236+
```
237+
238+
239+
#### `ReplaceFilter` (type filter)
173240

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

@@ -208,7 +275,8 @@ $myCopy = $deepCopy->copy($myObject);
208275

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

211-
#### `ShallowCopyFilter`
278+
279+
#### `ShallowCopyFilter` (type filter)
212280

213281
Stop *DeepCopy* from recursively copying element, using standard `clone` instead:
214282

@@ -228,59 +296,12 @@ $myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDep
228296
// all mocks will be just cloned, not deep-copied
229297
```
230298

231-
#### `DoctrineCollectionFilter`
232-
233-
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
234-
235-
```php
236-
use DeepCopy\DeepCopy;
237-
use DeepCopy\Filter\Doctrine\DoctrineCollectionFilter;
238-
use DeepCopy\Matcher\PropertyTypeMatcher;
239-
240-
$deepCopy = new DeepCopy();
241-
$deepCopy->addFilter(new DoctrineCollectionFilter(), new PropertyTypeMatcher('Doctrine\Common\Collections\Collection'));
242-
$myCopy = $deepCopy->copy($myObject);
243-
```
244-
245-
#### `DoctrineEmptyCollectionFilter`
246-
247-
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`
248-
249-
```php
250-
use DeepCopy\DeepCopy;
251-
use DeepCopy\Filter\Doctrine\DoctrineEmptyCollectionFilter;
252-
use DeepCopy\Matcher\PropertyMatcher;
253-
254-
$deepCopy = new DeepCopy();
255-
$deepCopy->addFilter(new DoctrineEmptyCollectionFilter(), new PropertyMatcher('MyClass', 'myProperty'));
256-
$myCopy = $deepCopy->copy($myObject);
257-
258-
// $myCopy->myProperty will return an empty collection
259-
```
260-
261-
#### `DoctrineProxyFilter`
262-
263-
If you use Doctrine and use cloning on lazy loaded entities, you might encounter errors mentioning missing fields on a
264-
Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
265-
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
266-
**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!**
267-
268-
```php
269-
use DeepCopy\DeepCopy;
270-
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
271-
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
272-
273-
$deepCopy = new DeepCopy();
274-
$deepCopy->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
275-
$myCopy = $deepCopy->copy($myObject);
276-
277-
// $myCopy should now contain a clone of all entities, including those that were not yet fully loaded.
278-
```
279299

280300
## Contributing
281301

282302
DeepCopy is distributed under the MIT license.
283303

304+
284305
### Tests
285306

286307
Running the tests is simple:

composer.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@
1515
},
1616
"autoload-dev": {
1717
"psr-4": {
18+
"DeepCopy\\": "fixtures/",
1819
"DeepCopyTest\\": "tests/DeepCopyTest/"
1920
}
2021
},
2122

2223
"require": {
23-
"php": ">=5.4.0"
24+
"php": "^5.6 || ^7.0"
2425
},
2526
"require-dev": {
2627
"doctrine/collections": "^1.0",
27-
"phpunit/phpunit": "~4.1"
28+
"doctrine/common": "^2.6",
29+
"phpunit/phpunit": "^4.1"
2830
},
2931

3032
"config": {

fixtures/f001/A.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace DeepCopy\f001;
4+
5+
class A
6+
{
7+
private $aProp;
8+
9+
public function getAProp()
10+
{
11+
return $this->aProp;
12+
}
13+
14+
public function setAProp($prop)
15+
{
16+
$this->aProp = $prop;
17+
18+
return $this;
19+
}
20+
}

fixtures/f001/B.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace DeepCopy\f001;
4+
5+
class B extends A
6+
{
7+
private $bProp;
8+
9+
public function getBProp()
10+
{
11+
return $this->bProp;
12+
}
13+
14+
public function setBProp($prop)
15+
{
16+
$this->bProp = $prop;
17+
18+
return $this;
19+
}
20+
}

fixtures/f002/A.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace DeepCopy\f002;
4+
5+
class A
6+
{
7+
private $prop1;
8+
private $prop2;
9+
10+
public function getProp1()
11+
{
12+
return $this->prop1;
13+
}
14+
15+
public function setProp1($prop)
16+
{
17+
$this->prop1 = $prop;
18+
19+
return $this;
20+
}
21+
22+
public function getProp2()
23+
{
24+
return $this->prop2;
25+
}
26+
27+
public function setProp2($prop)
28+
{
29+
$this->prop2 = $prop;
30+
31+
return $this;
32+
}
33+
}

fixtures/f003/Foo.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace DeepCopy\f003;
4+
5+
class Foo
6+
{
7+
private $name;
8+
private $prop;
9+
10+
public function __construct($name)
11+
{
12+
$this->name = $name;
13+
}
14+
15+
public function getProp()
16+
{
17+
return $this->prop;
18+
}
19+
20+
public function setProp($prop)
21+
{
22+
$this->prop = $prop;
23+
24+
return $this;
25+
}
26+
}

0 commit comments

Comments
 (0)