Completed
Pull Request — master (#294)
by Frank
02:10
created

  A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
dl 10
loc 10
c 0
b 0
f 0
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
3
namespace Tests\Prophecy\Doubler\Generator;
4
5
use Prophecy\Doubler\Generator\ClassMirror;
6
7
class ClassMirrorTest extends \PHPUnit_Framework_TestCase
8
{
9
    /**
10
     * @test
11
     */
12
    public function it_reflects_protected_abstract_methods()
13
    {
14
        $class = new \ReflectionClass('Fixtures\Prophecy\WithProtectedAbstractMethod');
15
16
        $mirror = new ClassMirror();
17
18
        $classNode = $mirror->reflect($class, array());
19
20
        $this->assertEquals('Fixtures\Prophecy\WithProtectedAbstractMethod', $classNode->getParentClass());
21
22
        $methodNodes = $classNode->getMethods();
23
        $this->assertCount(1, $methodNodes);
24
25
        $this->assertEquals('protected', $methodNodes['innerDetail']->getVisibility());
26
    }
27
28
    /**
29
     * @test
30
     */
31
    public function it_reflects_public_static_methods()
32
    {
33
        $class = new \ReflectionClass('Fixtures\Prophecy\WithStaticMethod');
34
35
        $mirror = new ClassMirror();
36
37
        $classNode = $mirror->reflect($class, array());
38
39
        $this->assertEquals('Fixtures\Prophecy\WithStaticMethod', $classNode->getParentClass());
40
41
        $methodNodes = $classNode->getMethods();
42
        $this->assertCount(1, $methodNodes);
43
44
        $this->assertTrue($methodNodes['innerDetail']->isStatic());
45
    }
46
47
    /**
48
     * @test
49
     */
50 View Code Duplication
    public function it_marks_required_args_without_types_as_not_optional()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
51
    {
52
        $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments');
53
54
        $mirror = new ClassMirror();
55
56
        $classNode = $mirror->reflect($class, array());
57
        $methodNode = $classNode->getMethod('methodWithoutTypeHints');
58
        $argNodes = $methodNode->getArguments();
59
60
        $this->assertCount(1, $argNodes);
61
62
        $this->assertEquals('arg', $argNodes[0]->getName());
63
        $this->assertNull($argNodes[0]->getTypeHint());
64
        $this->assertFalse($argNodes[0]->isOptional());
65
        $this->assertNull($argNodes[0]->getDefault());
66
        $this->assertFalse($argNodes[0]->isPassedByReference());
67
        $this->assertFalse($argNodes[0]->isVariadic());
68
    }
69
70
    /**
71
     * @test
72
     */
73
    public function it_properly_reads_methods_arguments_with_types()
74
    {
75
        $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments');
76
77
        $mirror = new ClassMirror();
78
79
        $classNode = $mirror->reflect($class, array());
80
        $methodNode = $classNode->getMethod('methodWithArgs');
81
        $argNodes = $methodNode->getArguments();
82
83
        $this->assertCount(3, $argNodes);
84
85
        $this->assertEquals('arg_1', $argNodes[0]->getName());
86
        $this->assertEquals('array', $argNodes[0]->getTypeHint());
87
        $this->assertTrue($argNodes[0]->isOptional());
88
        $this->assertEquals(array(), $argNodes[0]->getDefault());
89
        $this->assertFalse($argNodes[0]->isPassedByReference());
90
        $this->assertFalse($argNodes[0]->isVariadic());
91
92
        $this->assertEquals('arg_2', $argNodes[1]->getName());
93
        $this->assertEquals('ArrayAccess', $argNodes[1]->getTypeHint());
94
        $this->assertFalse($argNodes[1]->isOptional());
95
96
        $this->assertEquals('arg_3', $argNodes[2]->getName());
97
        $this->assertEquals('ArrayAccess', $argNodes[2]->getTypeHint());
98
        $this->assertTrue($argNodes[2]->isOptional());
99
        $this->assertNull($argNodes[2]->getDefault());
100
        $this->assertFalse($argNodes[2]->isPassedByReference());
101
        $this->assertFalse($argNodes[2]->isVariadic());
102
    }
103
104
    /**
105
     * @test
106
     * @requires PHP 5.4
107
     */
108
    public function it_properly_reads_methods_arguments_with_callable_types()
109
    {
110
        $class = new \ReflectionClass('Fixtures\Prophecy\WithCallableArgument');
111
112
        $mirror = new ClassMirror();
113
114
        $classNode = $mirror->reflect($class, array());
115
        $methodNode = $classNode->getMethod('methodWithArgs');
116
        $argNodes = $methodNode->getArguments();
117
118
        $this->assertCount(2, $argNodes);
119
120
        $this->assertEquals('arg_1', $argNodes[0]->getName());
121
        $this->assertEquals('callable', $argNodes[0]->getTypeHint());
122
        $this->assertFalse($argNodes[0]->isOptional());
123
        $this->assertFalse($argNodes[0]->isPassedByReference());
124
        $this->assertFalse($argNodes[0]->isVariadic());
125
126
        $this->assertEquals('arg_2', $argNodes[1]->getName());
127
        $this->assertEquals('callable', $argNodes[1]->getTypeHint());
128
        $this->assertTrue($argNodes[1]->isOptional());
129
        $this->assertNull($argNodes[1]->getDefault());
130
        $this->assertFalse($argNodes[1]->isPassedByReference());
131
        $this->assertFalse($argNodes[1]->isVariadic());
132
    }
133
134
    /**
135
     * @test
136
     * @requires PHP 5.6
137
     */
138 View Code Duplication
    public function it_properly_reads_methods_variadic_arguments()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
139
    {
140
        $class = new \ReflectionClass('Fixtures\Prophecy\WithVariadicArgument');
141
142
        $mirror = new ClassMirror();
143
144
        $classNode = $mirror->reflect($class, array());
145
        $methodNode = $classNode->getMethod('methodWithArgs');
146
        $argNodes = $methodNode->getArguments();
147
148
        $this->assertCount(1, $argNodes);
149
150
        $this->assertEquals('args', $argNodes[0]->getName());
151
        $this->assertNull($argNodes[0]->getTypeHint());
152
        $this->assertFalse($argNodes[0]->isOptional());
153
        $this->assertFalse($argNodes[0]->isPassedByReference());
154
        $this->assertTrue($argNodes[0]->isVariadic());
155
    }
156
157
    /**
158
     * @test
159
     * @requires PHP 5.6
160
     */
161
    public function it_properly_reads_methods_typehinted_variadic_arguments()
162
    {
163
        if (defined('HHVM_VERSION_ID')) {
164
            $this->markTestSkipped('HHVM does not support typehints on variadic arguments.');
165
        }
166
167
        $class = new \ReflectionClass('Fixtures\Prophecy\WithTypehintedVariadicArgument');
168
169
        $mirror = new ClassMirror();
170
171
        $classNode = $mirror->reflect($class, array());
172
        $methodNode = $classNode->getMethod('methodWithTypeHintedArgs');
173
        $argNodes = $methodNode->getArguments();
174
175
        $this->assertCount(1, $argNodes);
176
177
        $this->assertEquals('args', $argNodes[0]->getName());
178
        $this->assertEquals('array', $argNodes[0]->getTypeHint());
179
        $this->assertFalse($argNodes[0]->isOptional());
180
        $this->assertFalse($argNodes[0]->isPassedByReference());
181
        $this->assertTrue($argNodes[0]->isVariadic());
182
    }
183
184
    /**
185
     * @test
186
     */
187 View Code Duplication
    public function it_marks_passed_by_reference_args_as_passed_by_reference()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
188
    {
189
        $class = new \ReflectionClass('Fixtures\Prophecy\WithReferences');
190
191
        $mirror = new ClassMirror();
192
193
        $classNode = $mirror->reflect($class, array());
194
195
        $this->assertTrue($classNode->hasMethod('methodWithReferenceArgument'));
196
197
        $argNodes = $classNode->getMethod('methodWithReferenceArgument')->getArguments();
198
199
        $this->assertCount(2, $argNodes);
200
201
        $this->assertTrue($argNodes[0]->isPassedByReference());
202
        $this->assertTrue($argNodes[1]->isPassedByReference());
203
    }
204
205
    /**
206
     * @test
207
     */
208 View Code Duplication
    public function it_throws_an_exception_if_class_is_final()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
209
    {
210
        $class = new \ReflectionClass('Fixtures\Prophecy\FinalClass');
211
212
        $mirror = new ClassMirror();
213
214
        $this->setExpectedException('Prophecy\Exception\Doubler\ClassMirrorException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
215
216
        $mirror->reflect($class, array());
217
    }
218
219
    /**
220
     * @test
221
     */
222 View Code Duplication
    public function it_ignores_final_methods()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
223
    {
224
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
225
226
        $mirror = new ClassMirror();
227
228
        $classNode = $mirror->reflect($class, array());
229
230
        $this->assertCount(0, $classNode->getMethods());
231
    }
232
233
    /**
234
     * @test
235
     */
236
    public function it_marks_final_methods_as_unextendable()
237
    {
238
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
239
240
        $mirror = new ClassMirror();
241
242
        $classNode = $mirror->reflect($class, array());
243
244
        $this->assertCount(1, $classNode->getUnextendableMethods());
245
        $this->assertFalse($classNode->isExtendable('finalImplementation'));
246
    }
247
248
    /**
249
     * @test
250
     */
251 View Code Duplication
    public function it_throws_an_exception_if_interface_provided_instead_of_class()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
252
    {
253
        $class = new \ReflectionClass('Fixtures\Prophecy\EmptyInterface');
254
255
        $mirror = new ClassMirror();
256
257
        $this->setExpectedException('Prophecy\Exception\InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
258
259
        $mirror->reflect($class, array());
260
    }
261
262
    /**
263
     * @test
264
     */
265
    public function it_reflects_all_interfaces_methods()
266
    {
267
        $mirror = new ClassMirror();
268
269
        $classNode = $mirror->reflect(null, array(
270
            new \ReflectionClass('Fixtures\Prophecy\Named'),
271
            new \ReflectionClass('Fixtures\Prophecy\ModifierInterface'),
272
        ));
273
274
        $this->assertEquals('stdClass', $classNode->getParentClass());
275
        $this->assertEquals(array(
276
            'Prophecy\Doubler\Generator\ReflectionInterface',
277
            'Fixtures\Prophecy\ModifierInterface',
278
            'Fixtures\Prophecy\Named',
279
        ), $classNode->getInterfaces());
280
281
        $this->assertCount(3, $classNode->getMethods());
282
        $this->assertTrue($classNode->hasMethod('getName'));
283
        $this->assertTrue($classNode->hasMethod('isAbstract'));
284
        $this->assertTrue($classNode->hasMethod('getVisibility'));
285
    }
286
287
    /**
288
     * @test
289
     */
290 View Code Duplication
    public function it_does_not_throw_exception_for_virtually_private_finals()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
291
    {
292
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalVirtuallyPrivateMethod');
293
294
        $mirror = new ClassMirror();
295
296
        $classNode = $mirror->reflect($class, array());
297
298
        $this->assertCount(0, $classNode->getMethods());
299
    }
300
301
    /**
302
     * @test
303
     * @requires PHP 7
304
     */
305
    public function it_reflects_return_typehints()
306
    {
307
        $class = new \ReflectionClass('Fixtures\Prophecy\WithReturnTypehints');
308
309
        $mirror = new ClassMirror();
310
311
        $classNode = $mirror->reflect($class, array());
312
313
        $this->assertCount(3, $classNode->getMethods());
314
        $this->assertTrue($classNode->hasMethod('getName'));
315
        $this->assertTrue($classNode->hasMethod('getSelf'));
316
        $this->assertTrue($classNode->hasMethod('getParent'));
317
318
        $this->assertEquals('string', $classNode->getMethod('getName')->getReturnType());
319
        $this->assertEquals('\Fixtures\Prophecy\WithReturnTypehints', $classNode->getMethod('getSelf')->getReturnType());
320
        $this->assertEquals('\Fixtures\Prophecy\EmptyClass', $classNode->getMethod('getParent')->getReturnType());
321
    }
322
323
    /**
324
     * @test
325
     */
326
    public function it_throws_an_exception_if_class_provided_in_interfaces_list()
327
    {
328
        $class = new \ReflectionClass('Fixtures\Prophecy\EmptyClass');
329
330
        $mirror = new ClassMirror();
331
332
        $this->setExpectedException('InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
333
334
        $mirror->reflect(null, array($class));
335
    }
336
337
    /**
338
     * @test
339
     */
340
    public function it_throws_an_exception_if_not_reflection_provided_as_interface()
341
    {
342
        $mirror = new ClassMirror();
343
344
        $this->setExpectedException('InvalidArgumentException');
0 ignored issues
show
Deprecated Code introduced by
The method PHPUnit_Framework_TestCase::setExpectedException() has been deprecated with message: Method deprecated since Release 5.2.0

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
345
346
        $mirror->reflect(null, array(null));
0 ignored issues
show
Documentation introduced by
array(null) is of type array<integer,null,{"0":"null"}>, but the function expects a array<integer,object<ReflectionClass>>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
347
    }
348
349
    /**
350
     * @test
351
     */
352 View Code Duplication
    public function it_doesnt_use_scalar_typehints()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
353
    {
354
        $mirror = new ClassMirror();
355
356
        $classNode = $mirror->reflect(new \ReflectionClass('ReflectionMethod'), array());
357
        $method = $classNode->getMethod('export');
358
        $arguments = $method->getArguments();
359
360
        $this->assertNull($arguments[0]->getTypeHint());
361
        $this->assertNull($arguments[1]->getTypeHint());
362
        $this->assertNull($arguments[2]->getTypeHint());
363
    }
364
365
    /**
366
     * @test
367
     */
368 View Code Duplication
    public function it_doesnt_fail_to_typehint_nonexistent_FQCN()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
369
    {
370
        $mirror = new ClassMirror();
371
372
        $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
373
        $method = $classNode->getMethod('iHaveAStrangeTypeHintedArg');
374
        $arguments = $method->getArguments();
375
        $this->assertEquals('I\Simply\Am\Nonexistent', $arguments[0]->getTypeHint());
376
    }
377
378
    /**
379
     * @test
380
     */
381 View Code Duplication
    public function it_doesnt_fail_to_typehint_nonexistent_RQCN()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
382
    {
383
        $mirror = new ClassMirror();
384
385
        $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
386
        $method = $classNode->getMethod('iHaveAnEvenStrangerTypeHintedArg');
387
        $arguments = $method->getArguments();
388
        $this->assertEquals('I\Simply\Am\Not', $arguments[0]->getTypeHint());
389
    }
390
391
    /**
392
     * @test
393
     */
394
    function it_changes_argument_names_if_they_are_varying()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
395
    {
396
        // Use test doubles in this test, as arguments named ... in the Reflection API can only happen for internal classes
397
        $class = $this->prophesize('ReflectionClass');
398
        $method = $this->prophesize('ReflectionMethod');
399
        $parameter = $this->prophesize('ReflectionParameter');
400
401
        $class->getName()->willReturn('Custom\ClassName');
402
        $class->isInterface()->willReturn(false);
403
        $class->isFinal()->willReturn(false);
404
        $class->getMethods(\ReflectionMethod::IS_PUBLIC)->willReturn(array($method));
405
        $class->getMethods(\ReflectionMethod::IS_ABSTRACT)->willReturn(array());
406
407
        $method->getParameters()->willReturn(array($parameter));
408
        $method->getName()->willReturn('methodName');
409
        $method->isFinal()->willReturn(false);
410
        $method->isProtected()->willReturn(false);
411
        $method->isStatic()->willReturn(false);
412
        $method->returnsReference()->willReturn(false);
413
414
        if (version_compare(PHP_VERSION, '7.0', '>=')) {
415
            $method->hasReturnType()->willReturn(false);
416
        }
417
418
        $parameter->getName()->willReturn('...');
419
        $parameter->isDefaultValueAvailable()->willReturn(true);
420
        $parameter->getDefaultValue()->willReturn(null);
421
        $parameter->isPassedByReference()->willReturn(false);
422
        $parameter->getClass()->willReturn($class);
423
        if (version_compare(PHP_VERSION, '5.6', '>=')) {
424
            $parameter->isVariadic()->willReturn(false);
425
        }
426
427
        $mirror = new ClassMirror();
428
429
        $classNode = $mirror->reflect($class->reveal(), array());
430
431
        $methodNodes = $classNode->getMethods();
432
433
        $argumentNodes = $methodNodes['methodName']->getArguments();
434
        $argumentNode = $argumentNodes[0];
435
436
        $this->assertEquals('__dot_dot_dot__', $argumentNode->getName());
437
    }
438
}
439