1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace DeepCopyTest; |
4
|
|
|
|
5
|
|
|
use DateTime; |
6
|
|
|
use DateTimeImmutable; |
7
|
|
|
use DeepCopy\DeepCopy; |
8
|
|
|
use DeepCopy\Exception\CloneException; |
9
|
|
|
use DeepCopy\f001; |
10
|
|
|
use DeepCopy\f002; |
11
|
|
|
use DeepCopy\f003; |
12
|
|
|
use DeepCopy\f004; |
13
|
|
|
use DeepCopy\f005; |
14
|
|
|
use DeepCopy\f006; |
15
|
|
|
use DeepCopy\Filter\KeepFilter; |
16
|
|
|
use DeepCopy\Filter\SetNullFilter; |
17
|
|
|
use DeepCopy\Matcher\PropertyNameMatcher; |
18
|
|
|
use DeepCopy\TypeFilter\ShallowCopyFilter; |
19
|
|
|
use DeepCopy\TypeMatcher\TypeMatcher; |
20
|
|
|
use PHPUnit_Framework_TestCase; |
21
|
|
|
use SplDoublyLinkedList; |
22
|
|
|
use stdClass; |
23
|
|
|
use function DeepCopy\deep_copy; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @covers \DeepCopy\DeepCopy |
27
|
|
|
*/ |
28
|
|
|
class DeepCopyTest extends PHPUnit_Framework_TestCase |
29
|
|
|
{ |
30
|
|
|
/** |
31
|
|
|
* @dataProvider provideScalarValues |
32
|
|
|
*/ |
33
|
|
|
public function test_it_can_copy_scalar_values($value) |
34
|
|
|
{ |
35
|
|
|
$copy = deep_copy($value); |
36
|
|
|
|
37
|
|
|
$this->assertSame($value, $copy); |
38
|
|
|
} |
39
|
|
|
|
40
|
|
|
public function provideScalarValues() |
41
|
|
|
{ |
42
|
|
|
return [ |
43
|
|
|
[true], |
44
|
|
|
['string'], |
45
|
|
|
[null], |
46
|
|
|
[10], |
47
|
|
|
[-1], |
48
|
|
|
[.5], |
49
|
|
|
]; |
50
|
|
|
} |
51
|
|
|
|
52
|
|
|
public function test_it_can_copy_an_array_of_scalar_values() |
53
|
|
|
{ |
54
|
|
|
$copy = deep_copy([10, 20]); |
55
|
|
|
|
56
|
|
|
$this->assertSame([10, 20], $copy); |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
public function test_it_can_copy_an_object() |
60
|
|
|
{ |
61
|
|
|
$object = new stdClass(); |
62
|
|
|
|
63
|
|
|
$copy = deep_copy($object); |
64
|
|
|
|
65
|
|
|
$this->assertEqualButNotSame($object, $copy); |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
public function test_it_can_copy_an_array_of_objects() |
69
|
|
|
{ |
70
|
|
|
$object = [new stdClass()]; |
71
|
|
|
|
72
|
|
|
$copy = deep_copy($object); |
73
|
|
|
|
74
|
|
|
$this->assertEqualButNotSame($object, $copy); |
75
|
|
|
$this->assertEqualButNotSame($object[0], $copy[0]); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @dataProvider provideObjectWithScalarValues |
80
|
|
|
*/ |
81
|
|
|
public function test_it_can_copy_an_object_with_scalar_properties($object, $expectedVal) |
82
|
|
|
{ |
83
|
|
|
$copy = deep_copy($object); |
84
|
|
|
|
85
|
|
|
$this->assertEqualButNotSame($object, $copy); |
86
|
|
|
$this->assertSame($expectedVal, $copy->prop); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
public function provideObjectWithScalarValues() |
90
|
|
|
{ |
91
|
|
|
$createObject = function ($val) { |
92
|
|
|
$object = new stdClass(); |
93
|
|
|
|
94
|
|
|
$object->prop = $val; |
95
|
|
|
|
96
|
|
|
return $object; |
97
|
|
|
}; |
98
|
|
|
|
99
|
|
|
return array_map( |
100
|
|
|
function (array $vals) use ($createObject) { |
101
|
|
|
return [$createObject($vals[0]), $vals[0]]; |
102
|
|
|
}, |
103
|
|
|
$this->provideScalarValues() |
104
|
|
|
); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
public function test_it_can_copy_an_object_with_an_object_property() |
108
|
|
|
{ |
109
|
|
|
$foo = new stdClass(); |
110
|
|
|
$bar = new stdClass(); |
111
|
|
|
|
112
|
|
|
$foo->bar = $bar; |
113
|
|
|
|
114
|
|
|
$copy = deep_copy($foo); |
115
|
|
|
|
116
|
|
|
$this->assertEqualButNotSame($foo, $copy); |
117
|
|
|
$this->assertEqualButNotSame($foo->bar, $copy->bar); |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public function test_dynamic_properties_are_copied() |
121
|
|
|
{ |
122
|
|
|
$foo = new stdClass(); |
123
|
|
|
$bar = new stdClass(); |
124
|
|
|
|
125
|
|
|
$foo->bar = $bar; |
126
|
|
|
|
127
|
|
|
$copy = deep_copy($foo); |
128
|
|
|
|
129
|
|
|
$this->assertEqualButNotSame($foo, $copy); |
130
|
|
|
$this->assertEqualButNotSame($foo->bar, $copy->bar); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @ticket https://github.com/myclabs/DeepCopy/issues/38 |
135
|
|
|
*/ |
136
|
|
|
public function test_it_can_copy_an_object_with_a_date_object_property() |
137
|
|
|
{ |
138
|
|
|
$object = new stdClass(); |
139
|
|
|
|
140
|
|
|
$object->d1 = new DateTime(); |
141
|
|
|
$object->d2 = new DateTimeImmutable(); |
142
|
|
|
|
143
|
|
|
$copy = deep_copy($object); |
144
|
|
|
|
145
|
|
|
$this->assertEqualButNotSame($object->d1, $copy->d1); |
146
|
|
|
$this->assertEqualButNotSame($object->d2, $copy->d2); |
147
|
|
|
} |
148
|
|
|
|
149
|
|
|
public function test_it_copies_the_private_properties_of_the_parent_class() |
150
|
|
|
{ |
151
|
|
|
$object = new f001\B(); |
152
|
|
|
|
153
|
|
|
$object->setAProp($aStdClass = new stdClass()); |
154
|
|
|
$object->setBProp($bStdClass = new stdClass()); |
155
|
|
|
|
156
|
|
|
/** @var f001\B $copy */ |
157
|
|
|
$copy = deep_copy($object); |
158
|
|
|
|
159
|
|
|
$this->assertEqualButNotSame($aStdClass, $copy->getAProp()); |
160
|
|
|
$this->assertEqualButNotSame($bStdClass, $copy->getBProp()); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
public function test_it_keeps_reference_of_the_copied_objects_when_copying_the_graph() |
164
|
|
|
{ |
165
|
|
|
$a = new f002\A(); |
166
|
|
|
|
167
|
|
|
$b = new stdClass(); |
168
|
|
|
$c = new stdClass(); |
169
|
|
|
|
170
|
|
|
$a->setProp1($b); |
171
|
|
|
$a->setProp2($c); |
172
|
|
|
|
173
|
|
|
$b->c = $c; |
174
|
|
|
|
175
|
|
|
/** @var f002\A $copy */ |
176
|
|
|
$copy = deep_copy($a); |
177
|
|
|
|
178
|
|
|
$this->assertEqualButNotSame($a, $copy); |
179
|
|
|
$this->assertEqualButNotSame($b, $copy->getProp1()); |
180
|
|
|
$this->assertEqualButNotSame($c, $copy->getProp2()); |
181
|
|
|
|
182
|
|
|
$this->assertSame($copy->getProp1()->c, $copy->getProp2()); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
public function test_it_can_copy_graphs_with_circular_references() |
186
|
|
|
{ |
187
|
|
|
$a = new stdClass(); |
188
|
|
|
$b = new stdClass(); |
189
|
|
|
|
190
|
|
|
$a->prop = $b; |
191
|
|
|
$b->prop = $a; |
192
|
|
|
|
193
|
|
|
$copy = deep_copy($a); |
194
|
|
|
|
195
|
|
|
$this->assertEqualButNotSame($a, $copy); |
196
|
|
|
$this->assertEqualButNotSame($b, $copy->prop); |
197
|
|
|
|
198
|
|
|
$this->assertSame($copy, $copy->prop->prop); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
public function test_it_can_copy_graphs_with_circular_references_with_userland_class() |
202
|
|
|
{ |
203
|
|
|
$a = new f003\Foo('a'); |
204
|
|
|
$b = new f003\Foo('b'); |
205
|
|
|
|
206
|
|
|
$a->setProp($b); |
207
|
|
|
$b->setProp($a); |
208
|
|
|
|
209
|
|
|
/** @var f003\Foo $copy */ |
210
|
|
|
$copy = deep_copy($a); |
211
|
|
|
|
212
|
|
|
$this->assertEqualButNotSame($a, $copy); |
213
|
|
|
$this->assertEqualButNotSame($b, $copy->getProp()); |
214
|
|
|
|
215
|
|
|
$this->assertSame($copy, $copy->getProp()->getProp()); |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
public function test_it_cannot_copy_unclonable_items() |
219
|
|
|
{ |
220
|
|
|
$object = new f004\UnclonableItem(); |
221
|
|
|
|
222
|
|
|
try { |
223
|
|
|
deep_copy($object); |
224
|
|
|
|
225
|
|
|
$this->fail('Expected exception to be thrown.'); |
226
|
|
|
} catch (CloneException $exception) { |
227
|
|
|
$this->assertSame( |
228
|
|
|
sprintf( |
229
|
|
|
'The class "%s" is not cloneable.', |
230
|
|
|
f004\UnclonableItem::class |
231
|
|
|
), |
232
|
|
|
$exception->getMessage() |
233
|
|
|
); |
234
|
|
|
$this->assertSame(0, $exception->getCode()); |
235
|
|
|
$this->assertNull($exception->getPrevious()); |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
public function test_it_can_skip_uncloneable_objects() |
240
|
|
|
{ |
241
|
|
|
$object = new f004\UnclonableItem(); |
242
|
|
|
|
243
|
|
|
$deepCopy = new DeepCopy(); |
244
|
|
|
$deepCopy->skipUncloneable(true); |
245
|
|
|
|
246
|
|
|
$copy = $deepCopy->copy($object); |
247
|
|
|
|
248
|
|
|
$this->assertSame($object, $copy); |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
public function test_it_uses_the_userland_defined_cloned_method() |
252
|
|
|
{ |
253
|
|
|
$object = new f005\Foo(); |
254
|
|
|
|
255
|
|
|
$copy = deep_copy($object); |
256
|
|
|
|
257
|
|
|
$this->assertTrue($copy->cloned); |
258
|
|
|
} |
259
|
|
|
|
260
|
|
|
public function test_it_only_uses_the_userland_defined_cloned_method_when_configured_to_do_so() |
261
|
|
|
{ |
262
|
|
|
$object = new f005\Foo(); |
263
|
|
|
$object->foo = new stdClass(); |
|
|
|
|
264
|
|
|
|
265
|
|
|
$copy = deep_copy($object, true); |
266
|
|
|
|
267
|
|
|
$this->assertTrue($copy->cloned); |
268
|
|
|
$this->assertSame($object->foo, $copy->foo); |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
public function test_it_uses_type_filter_to_copy_objects_if_matcher_matches() |
272
|
|
|
{ |
273
|
|
|
$deepCopy = new DeepCopy(); |
274
|
|
|
$deepCopy->addTypeFilter( |
275
|
|
|
new ShallowCopyFilter(), |
276
|
|
|
new TypeMatcher(f006\A::class) |
277
|
|
|
); |
278
|
|
|
|
279
|
|
|
$a = new f006\A; |
280
|
|
|
$b = new f006\B; |
281
|
|
|
|
282
|
|
|
$a->setAProp($b); |
283
|
|
|
|
284
|
|
|
/** @var f006\A $copy */ |
285
|
|
|
$copy = $deepCopy->copy($a); |
286
|
|
|
|
287
|
|
|
$this->assertTrue($copy->cloned); |
288
|
|
|
$this->assertFalse($copy->getAProp()->cloned); |
289
|
|
|
$this->assertSame($b, $copy->getAProp()); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
public function test_it_uses_filters_to_copy_object_properties_if_matcher_matches() |
293
|
|
|
{ |
294
|
|
|
$deepCopy = new DeepCopy(); |
295
|
|
|
$deepCopy->addFilter( |
296
|
|
|
new SetNullFilter(), |
297
|
|
|
new PropertyNameMatcher('cloned') |
298
|
|
|
); |
299
|
|
|
|
300
|
|
|
$a = new f006\A; |
301
|
|
|
$b = new f006\B; |
302
|
|
|
|
303
|
|
|
$a->setAProp($b); |
304
|
|
|
|
305
|
|
|
/** @var f006\A $copy */ |
306
|
|
|
$copy = $deepCopy->copy($a); |
307
|
|
|
|
308
|
|
|
$this->assertNull($copy->cloned); |
309
|
|
|
$this->assertNull($copy->getAProp()->cloned); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
public function test_it_uses_the_first_filter_matching_for_copying_object_properties() |
313
|
|
|
{ |
314
|
|
|
$deepCopy = new DeepCopy(); |
315
|
|
|
$deepCopy->addFilter( |
316
|
|
|
new SetNullFilter(), |
317
|
|
|
new PropertyNameMatcher('cloned') |
318
|
|
|
); |
319
|
|
|
$deepCopy->addFilter( |
320
|
|
|
new KeepFilter(), |
321
|
|
|
new PropertyNameMatcher('cloned') |
322
|
|
|
); |
323
|
|
|
|
324
|
|
|
$a = new f006\A; |
325
|
|
|
$b = new f006\B; |
326
|
|
|
|
327
|
|
|
$a->setAProp($b); |
328
|
|
|
|
329
|
|
|
/** @var f006\A $copy */ |
330
|
|
|
$copy = $deepCopy->copy($a); |
331
|
|
|
|
332
|
|
|
$this->assertNull($copy->cloned); |
333
|
|
|
$this->assertNull($copy->getAProp()->cloned); |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* @ticket https://github.com/myclabs/DeepCopy/pull/49 |
338
|
|
|
*/ |
339
|
|
|
public function test_it_can_copy_a_SplDoublyLinkedList() |
340
|
|
|
{ |
341
|
|
|
$object = new SplDoublyLinkedList(); |
342
|
|
|
|
343
|
|
|
$a = new stdClass(); |
344
|
|
|
$b = new stdClass(); |
345
|
|
|
|
346
|
|
|
$a->b = $b; |
347
|
|
|
|
348
|
|
|
$object->push($a); |
349
|
|
|
|
350
|
|
|
/** @var SplDoublyLinkedList $copy */ |
351
|
|
|
$copy = deep_copy($object); |
352
|
|
|
|
353
|
|
|
$this->assertEqualButNotSame($object, $copy); |
354
|
|
|
|
355
|
|
|
$aCopy = $copy->pop(); |
356
|
|
|
|
357
|
|
|
$this->assertEqualButNotSame($b, $aCopy->b); |
358
|
|
|
} |
359
|
|
|
|
360
|
|
|
private function assertEqualButNotSame($expected, $val) |
361
|
|
|
{ |
362
|
|
|
$this->assertEquals($expected, $val); |
363
|
|
|
$this->assertNotSame($expected, $val); |
364
|
|
|
} |
365
|
|
|
} |
366
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.