Skip to content

Commit 86e91ea

Browse files
committed
Fix copying of DateInterval objects
1 parent c04239a commit 86e91ea

File tree

8 files changed

+127
-11
lines changed

8 files changed

+127
-11
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ DeepCopy helps you create deep copies (clones) of your objects. It is designed t
2727
1. [`DoctrineProxyFilter`](#doctrineproxyfilter-filter)
2828
1. [`ReplaceFilter`](#replacefilter-type-filter)
2929
1. [`ShallowCopyFilter`](#doctrinecollectionfilter-type-filter)
30+
1. [Edge cases](#edge-cases)
3031
1. [Contributing](#contributing)
3132
1. [Tests](#tests)
3233

@@ -297,6 +298,15 @@ $myServiceWithMocks = new MyService(m::mock(MyDependency1::class), m::mock(MyDep
297298
```
298299

299300

301+
## Edge cases
302+
303+
The following structures cannot be deep-copied with PHP Reflection. As a result they are shallow cloned and filters are
304+
not applied. There is two ways for you to handle them:
305+
306+
- Implement your own `__clone()` method
307+
- Use a filter with a type matcher
308+
309+
300310
## Contributing
301311

302312
DeepCopy is distributed under the MIT license.

fixtures/f007/FooDateInterval.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace DeepCopy\f007;
4+
5+
use DateInterval;
6+
7+
class FooDateInterval extends DateInterval
8+
{
9+
public $cloned = false;
10+
11+
public function __clone()
12+
{
13+
$this->cloned = true;
14+
}
15+
}

src/DeepCopy/DeepCopy.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
namespace DeepCopy;
44

5+
use DateInterval;
56
use DateTimeInterface;
67
use DateTimeZone;
78
use DeepCopy\Exception\CloneException;
89
use DeepCopy\Filter\Filter;
910
use DeepCopy\Matcher\Matcher;
11+
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
1012
use DeepCopy\TypeFilter\Spl\SplDoublyLinkedListFilter;
1113
use DeepCopy\TypeFilter\TypeFilter;
1214
use DeepCopy\TypeMatcher\TypeMatcher;
@@ -57,6 +59,7 @@ public function __construct($useCloneMethod = false)
5759
{
5860
$this->useCloneMethod = $useCloneMethod;
5961

62+
$this->addTypeFilter(new DateIntervalFilter(), new TypeMatcher(DateInterval::class));
6063
$this->addTypeFilter(new SplDoublyLinkedListFilter($this), new TypeMatcher(SplDoublyLinkedList::class));
6164
}
6265

@@ -186,7 +189,10 @@ private function copyObject($object)
186189
return $newObject;
187190
}
188191

189-
if ($newObject instanceof DateTimeInterface || get_class($newObject) === DateTimeZone::class) {
192+
if ($newObject instanceof DateTimeInterface
193+
|| $newObject instanceof DateTimeZone
194+
|| $newObject instanceof DateInterval
195+
) {
190196
return $newObject;
191197
}
192198

@@ -225,7 +231,6 @@ function ($object) {
225231
}
226232
}
227233

228-
// No filter has been applied: simply copy the value
229234
$property->setAccessible(true);
230235
$propertyValue = $property->getValue($object);
231236

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace DeepCopy\TypeFilter\Date;
4+
5+
use DateInterval;
6+
use DeepCopy\TypeFilter\TypeFilter;
7+
8+
/**
9+
* @final
10+
*
11+
* @deprecated Will be removed in 2.0. This filter will no longer be necessary in PHP 7.1+.
12+
*/
13+
class DateIntervalFilter implements TypeFilter
14+
{
15+
16+
/**
17+
* {@inheritdoc}
18+
*
19+
* @param DateInterval $element
20+
*
21+
* @see http://news.php.net/php.bugs/205076
22+
*/
23+
public function apply($element)
24+
{
25+
$copy = new DateInterval('P0D');
26+
27+
foreach ($element as $propertyName => $propertyValue) {
28+
$copy->{$propertyName} = $propertyValue;
29+
}
30+
31+
return $copy;
32+
}
33+
}

src/DeepCopy/TypeFilter/Spl/SplDoublyLinkedListFilter.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@
1111
*/
1212
class SplDoublyLinkedListFilter implements TypeFilter
1313
{
14-
/**
15-
* @var DeepCopy
16-
*/
1714
private $deepCopy;
1815

1916
public function __construct(DeepCopy $deepCopy)

tests/DeepCopyTest/DeepCopyTest.php

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace DeepCopyTest;
44

5+
use DateInterval;
56
use DateTime;
67
use DateTimeImmutable;
78
use DateTimeZone;
@@ -13,6 +14,7 @@
1314
use DeepCopy\f004;
1415
use DeepCopy\f005;
1516
use DeepCopy\f006;
17+
use DeepCopy\f007;
1618
use DeepCopy\f008;
1719
use DeepCopy\Filter\KeepFilter;
1820
use DeepCopy\Filter\SetNullFilter;
@@ -136,6 +138,7 @@ public function test_it_copies_dynamic_properties()
136138
/**
137139
* @ticket https://github.com/myclabs/DeepCopy/issues/38
138140
* @ticket https://github.com/myclabs/DeepCopy/pull/70
141+
* @ticket https://github.com/myclabs/DeepCopy/pull/76
139142
*/
140143
public function test_it_can_copy_an_object_with_a_date_object_property()
141144
{
@@ -144,26 +147,54 @@ public function test_it_can_copy_an_object_with_a_date_object_property()
144147
$object->d1 = new DateTime();
145148
$object->d2 = new DateTimeImmutable();
146149
$object->dtz = new DateTimeZone('UTC');
150+
$object->di = new DateInterval('P2D');
147151

148152
$copy = deep_copy($object);
149153

150154
$this->assertEqualButNotSame($object->d1, $copy->d1);
151155
$this->assertEqualButNotSame($object->d2, $copy->d2);
152156
$this->assertEqualButNotSame($object->dtz, $copy->dtz);
157+
$this->assertEqualButNotSame($object->di, $copy->di);
153158
}
154159

155160
/**
156-
* @ticket https://github.com/myclabs/DeepCopy/pull/70g
161+
* @ticket https://github.com/myclabs/DeepCopy/pull/70
157162
*/
158-
public function test_it_does_not_skip_the_copy_for_userland_datetimezone()
163+
public function test_it_skips_the_copy_for_userland_datetimezone()
159164
{
165+
$deepCopy = new DeepCopy();
166+
$deepCopy->addFilter(
167+
new SetNullFilter(),
168+
new PropertyNameMatcher('cloned')
169+
);
170+
160171
$object = new stdClass();
161172

162-
$object->dtz = new DateTimeZone('UTC');
173+
$object->dtz = new f007\FooDateTimeZone('UTC');
163174

164-
$copy = deep_copy($object);
175+
$copy = $deepCopy->copy($object);
165176

166-
$this->assertEqualButNotSame($object->dtz, $copy->dtz);
177+
$this->assertTrue($copy->dtz->cloned);
178+
}
179+
180+
/**
181+
* @ticket https://github.com/myclabs/DeepCopy/pull/76
182+
*/
183+
public function test_it_skips_the_copy_for_userland_dateinterval()
184+
{
185+
$deepCopy = new DeepCopy();
186+
$deepCopy->addFilter(
187+
new SetNullFilter(),
188+
new PropertyNameMatcher('cloned')
189+
);
190+
191+
$object = new stdClass();
192+
193+
$object->di = new f007\FooDateInterval('P2D');
194+
195+
$copy = $deepCopy->copy($object);
196+
197+
$this->assertFalse($copy->di->cloned);
167198
}
168199

169200
public function test_it_copies_the_private_properties_of_the_parent_class()
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace DeepCopyTest\TypeFilter\Date;
4+
5+
use DateInterval;
6+
use DeepCopy\TypeFilter\Date\DateIntervalFilter;
7+
use PHPUnit_Framework_TestCase;
8+
9+
/**
10+
* @covers \DeepCopy\TypeFilter\Date\DateIntervalFilter
11+
*/
12+
class DateIntervalFilterTest extends PHPUnit_Framework_TestCase
13+
{
14+
public function test_it_deep_copies_a_DateInterval()
15+
{
16+
$object = new DateInterval('P2D');;
17+
18+
$filter = new DateIntervalFilter();
19+
20+
$copy = $filter->apply($object);
21+
22+
$this->assertEquals($object, $copy);
23+
$this->assertNotSame($object, $copy);
24+
}
25+
}

tests/DeepCopyTest/TypeFilter/Spl/SplDoublyLinkedListFilterTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
*/
1414
class SplDoublyLinkedListFilterTest extends PHPUnit_Framework_TestCase
1515
{
16-
public function test_it_deeps_copy_a_doubly_linked_spl_list()
16+
public function test_it_deep_copies_a_doubly_linked_spl_list()
1717
{
1818
$foo = new stdClass();
1919

0 commit comments

Comments
 (0)