Completed
Push — master ( cd2604...399c1f )
by Matthieu
03:36
created

tests/DeepCopyTest/DeepCopyTest.php (2 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace DeepCopyTest;
4
5
use DeepCopy\DeepCopy;
6
use DeepCopy\Filter\Filter;
7
use DeepCopy\Matcher\PropertyMatcher;
8
use DeepCopy\TypeFilter\TypeFilter;
9
use DeepCopy\TypeMatcher\TypeMatcher;
10
11
/**
12
 * DeepCopyTest
13
 */
14
class DeepCopyTest extends AbstractTestClass
15
{
16
    public function testSimpleObjectCopy()
17
    {
18
        $o = new A();
19
20
        $deepCopy = new DeepCopy();
21
22
        $this->assertDeepCopyOf($o, $deepCopy->copy($o));
23
    }
24
25
    public function testPropertyScalarCopy()
26
    {
27
        $o = new A();
28
        $o->property1 = 'foo';
29
30
        $deepCopy = new DeepCopy();
31
32
        $this->assertDeepCopyOf($o, $deepCopy->copy($o));
33
    }
34
35
    public function testPropertyObjectCopy()
36
    {
37
        $o = new A();
38
        $o->property1 = new B();
39
40
        $deepCopy = new DeepCopy();
41
42
        $this->assertDeepCopyOf($o, $deepCopy->copy($o));
43
    }
44
45
    public function testPropertyObjectCopyWithDateTimes()
46
    {
47
        $o = new A();
48
        $o->date1 = new \DateTime();
49
        if (class_exists('DateTimeImmutable')) {
50
            $o->date2 = new \DateTimeImmutable();
51
        }
52
53
        $deepCopy = new DeepCopy();
54
        $c = $deepCopy->copy($o);
55
56
        $this->assertDeepCopyOf($o, $c);
57
58
        $c->date1->setDate(2015, 01, 04);
59
        $this->assertNotEquals($c->date1, $o->date1);
60
    }
61
62
    public function testPrivatePropertyOfParentObjectCopy()
63
    {
64
        $o = new E();
65
        $o->setProperty1(new B);
66
        $o->setProperty2(new B);
67
68
        $deepCopy = new DeepCopy();
69
70
        $this->assertDeepCopyOf($o, $deepCopy->copy($o));
71
    }
72
73
    public function testPropertyArrayCopy()
74
    {
75
        $o = new A();
76
        $o->property1 = [new B()];
77
78
        $deepCopy = new DeepCopy();
79
80
        $this->assertDeepCopyOf($o, $deepCopy->copy($o));
81
    }
82
83
    public function testCycleCopy1()
84
    {
85
        $a = new A();
86
        $b = new B();
87
        $c = new B();
88
        $a->property1 = $b;
89
        $a->property2 = $c;
90
        $b->property = $c;
91
92
        $deepCopy = new DeepCopy();
93
        /** @var A $a2 */
94
        $a2 = $deepCopy->copy($a);
95
96
        $this->assertDeepCopyOf($a, $a2);
97
98
        $this->assertSame($a2->property1->property, $a2->property2);
99
    }
100
101
    public function testCycleCopy2()
102
    {
103
        $a = new B();
104
        $b = new B();
105
        $a->property = $b;
106
        $b->property = $a;
107
108
        $deepCopy = new DeepCopy();
109
        /** @var B $a2 */
110
        $a2 = $deepCopy->copy($a);
111
112
        $this->assertSame($a2, $a2->property->property);
113
    }
114
115
    /**
116
     * Dynamic properties should be cloned
117
     */
118
    public function testDynamicProperties()
119
    {
120
        $a = new \stdClass();
121
        $a->b = new \stdClass();
122
123
        $deepCopy = new DeepCopy();
124
        $a2 = $deepCopy->copy($a);
125
        $this->assertNotSame($a->b, $a2->b);
126
        $this->assertDeepCopyOf($a, $a2);
127
    }
128
129
    public function testCloneChild()
130
    {
131
        $h = new H();
132
133
        $deepCopy = new DeepCopy();
134
        $newH = $deepCopy->copy($h);
135
136
        $propRefl = (new \ReflectionObject($newH))->getProperty('prop');
137
        $propRefl->setAccessible(true);
138
139
        $this->assertNotSame($newH, $h);
140
        $this->assertEquals($newH, $h);
141
        $this->assertEquals('bar', $propRefl->getValue($newH));
142
    }
143
144
    public function testNonClonableItems()
145
    {
146
        $a = new \ReflectionClass('DeepCopyTest\A');
147
        $deepCopy = new DeepCopy();
148
        $a2 = $deepCopy->skipUncloneable()->copy($a);
149
        $this->assertSame($a, $a2);
150
    }
151
152
    /**
153
     * @expectedException \DeepCopy\Exception\CloneException
154
     * @expectedExceptionMessage Class "DeepCopyTest\C" is not cloneable.
155
     */
156
    public function testCloneException()
157
    {
158
        $o = new C;
159
        $deepCopy = new DeepCopy();
160
        $deepCopy->copy($o);
161
    }
162
    
163
    public function testCloneObjectsWithUserlandCloneMethod()
164
    {
165
        $f = new F();
166
        $f->prop = new \DateTime('2016-09-16');
167
168
        $deepCopy = new DeepCopy();
169
        $newF = $deepCopy->copy($f);
170
171
        $this->assertNotSame($newF->prop, $f->prop);
172
    }
173
174
    public function testCloneObjectsWithUserlandCloneMethodAndUseCloneableMethodEnabled()
175
    {
176
        $f = new F();
177
        $f->prop = new \DateTime('2016-09-16');
178
179
        $deepCopy = new DeepCopy(true);
180
        $newF = $deepCopy->copy($f);
181
182
        $this->assertSame($newF->prop, $f->prop);
183
    }
184
185
    /**
186
     * @test
187
     */
188
    public function filtersShouldBeApplied()
189
    {
190
        $o = new A();
191
        $o->property1 = 'foo';
192
193
        $filter = $this->getMockForAbstractClass('DeepCopy\Filter\Filter');
194
        $filter->expects($this->once())
195
            ->method('apply')
196
            ->will($this->returnCallback(function($object, $property) {
197
                        $object->$property = null;
198
                    }));
199
200
        $deepCopy = new DeepCopy();
201
        $deepCopy->addFilter($filter, new PropertyMatcher(get_class($o), 'property1'));
202
        /** @var A $new */
203
        $new = $deepCopy->copy($o);
204
205
        $this->assertNull($new->property1);
206
    }
207
208
    /**
209
     * If a filter applies to a property, the property shouldn't be copied
210
     * @test
211
     */
212
    public function filtersShouldBeAppliedAndBreakPropertyCopying()
213
    {
214
        $o = new A();
215
        $o->property1 = new B();
216
217
        /* @var Filter|\PHPUnit_Framework_MockObject_MockObject $filter */
218
        $filter = $this->getMockForAbstractClass('DeepCopy\Filter\Filter');
219
        $filter->expects($this->once())
220
            ->method('apply')
221
            ->will($this->returnCallback(function($object, $property, $objectCopier) {
222
                    }));
223
224
        $deepCopy = new DeepCopy();
225
        $deepCopy->addFilter($filter, new PropertyMatcher(get_class($o), 'property1'));
226
        /** @var A $new */
227
        $new = $deepCopy->copy($o);
228
229
        $this->assertSame($o->property1, $new->property1);
230
    }
231
232
    /**
233
     * If a filter applies to an object, it should not be copied
234
     */
235
    public function testTypeFilterShouldBeAppliedOnObject()
236
    {
237
        $o = new A();
238
        $o->property1 = new B();
239
240
        /* @var TypeFilter|\PHPUnit_Framework_MockObject_MockObject $filter */
241
        $filter = $this->getMockForAbstractClass('DeepCopy\TypeFilter\TypeFilter');
242
        $filter->expects($this->once())
243
            ->method('apply')
244
            ->will($this->returnValue(null));
245
246
        $deepCopy = new DeepCopy();
247
        $deepCopy->addTypeFilter($filter, new TypeMatcher('DeepCopyTest\B'));
0 ignored issues
show
It seems like $filter defined by $this->getMockForAbstrac...ypeFilter\\TypeFilter') on line 241 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, DeepCopy\DeepCopy::addTypeFilter() does only seem to accept object<DeepCopy\TypeFilter\TypeFilter>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
248
        /** @var A $new */
249
        $new = $deepCopy->copy($o);
250
251
        $this->assertNull($new->property1);
252
    }
253
254
    /**
255
     * If a filter applies to an array member, it should not be copied
256
     */
257
    public function testTypeFilterShouldBeAppliedOnArrayMember()
258
    {
259
        $arr = [new A, new A, new B, new B, new A];
260
261
        /* @var TypeFilter|\PHPUnit_Framework_MockObject_MockObject $filter */
262
        $filter = $this->getMockForAbstractClass('DeepCopy\TypeFilter\TypeFilter');
263
        $filter->expects($this->exactly(2))
264
            ->method('apply')
265
            ->will($this->returnValue(null));
266
267
        $deepCopy = new DeepCopy();
268
        $deepCopy->addTypeFilter($filter, new TypeMatcher('DeepCopyTest\B'));
0 ignored issues
show
It seems like $filter defined by $this->getMockForAbstrac...ypeFilter\\TypeFilter') on line 262 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, DeepCopy\DeepCopy::addTypeFilter() does only seem to accept object<DeepCopy\TypeFilter\TypeFilter>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
269
        /** @var A $new */
270
        $new = $deepCopy->copy($arr);
271
272
        $this->assertNull($new[2]);
273
        $this->assertNull($new[3]);
274
    }
275
}
276
277
class A
278
{
279
    public $property1;
280
    public $property2;
281
}
282
283
class B
284
{
285
    public $property;
286
}
287
288
class C
289
{
290
    private function __clone(){}
291
}
292
293
class D
294
{
295
    private $property1;
296
297
    public function getProperty1()
298
    {
299
        return $this->property1;
300
    }
301
302
    public function setProperty1($property1)
303
    {
304
        $this->property1 = $property1;
305
        return $this;
306
    }
307
}
308
309
class E extends D
310
{
311
    private $property2;
312
313
    public function getProperty2()
314
    {
315
        return $this->property2;
316
    }
317
318
    public function setProperty2($property2)
319
    {
320
        $this->property2 = $property2;
321
        return $this;
322
    }
323
}
324
325
class F
326
{
327
    public $prop;
328
329
    public function __clone()
330
    {
331
        $this->foo = 'bar';
332
    }
333
}
334
335
class G
336
{
337
    private $prop = 'foo';
338
}
339
340
class H extends G
341
{
342
    private $prop = 'bar';
343
}
344