Completed
Pull Request — master (#274)
by Christophe
05:13
created

it_reflects_public_static_methods()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 1
eloc 8
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 View Code Duplication
    public function it_reflects_allowed_magic_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...
13
    {
14
        $class = new \ReflectionClass('Fixtures\Prophecy\SpecialMethods');
15
16
        $mirror = new ClassMirror();
17
18
        $node = $mirror->reflect($class, array());
19
20
        $this->assertCount(7, $node->getMethods());
21
    }
22
23
    /**
24
     * @test
25
     */
26
    public function it_reflects_protected_abstract_methods()
27
    {
28
        $class = new \ReflectionClass('Fixtures\Prophecy\WithProtectedAbstractMethod');
29
30
        $mirror = new ClassMirror();
31
32
        $classNode = $mirror->reflect($class, array());
33
34
        $this->assertEquals('Fixtures\Prophecy\WithProtectedAbstractMethod', $classNode->getParentClass());
35
36
        $methodNodes = $classNode->getMethods();
37
        $this->assertCount(1, $methodNodes);
38
39
        $this->assertEquals('protected', $methodNodes['innerDetail']->getVisibility());
40
    }
41
42
    /**
43
     * @test
44
     */
45
    public function it_reflects_public_static_methods()
46
    {
47
        $class = new \ReflectionClass('Fixtures\Prophecy\WithStaticMethod');
48
49
        $mirror = new ClassMirror();
50
51
        $classNode = $mirror->reflect($class, array());
52
53
        $this->assertEquals('Fixtures\Prophecy\WithStaticMethod', $classNode->getParentClass());
54
55
        $methodNodes = $classNode->getMethods();
56
        $this->assertCount(1, $methodNodes);
57
58
        $this->assertTrue($methodNodes['innerDetail']->isStatic());
59
    }
60
61
    /**
62
     * @test
63
     */
64
    public function it_marks_required_args_without_types_as_not_optional()
65
    {
66
        $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments');
67
68
        $mirror = new ClassMirror();
69
70
        $classNode = $mirror->reflect($class, array());
71
        $methodNode = $classNode->getMethod('methodWithoutTypeHints');
72
        $argNodes = $methodNode->getArguments();
73
74
        $this->assertCount(1, $argNodes);
75
76
        $this->assertEquals('arg', $argNodes[0]->getName());
77
        $this->assertNull($argNodes[0]->getTypeHint());
78
        $this->assertFalse($argNodes[0]->isOptional());
79
        $this->assertNull($argNodes[0]->getDefault());
80
        $this->assertFalse($argNodes[0]->isPassedByReference());
81
        $this->assertFalse($argNodes[0]->isVariadic());
82
    }
83
84
    /**
85
     * @test
86
     */
87
    public function it_properly_reads_methods_arguments_with_types()
88
    {
89
        $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments');
90
91
        $mirror = new ClassMirror();
92
93
        $classNode = $mirror->reflect($class, array());
94
        $methodNode = $classNode->getMethod('methodWithArgs');
95
        $argNodes = $methodNode->getArguments();
96
97
        $this->assertCount(3, $argNodes);
98
99
        $this->assertEquals('arg_1', $argNodes[0]->getName());
100
        $this->assertEquals('array', $argNodes[0]->getTypeHint());
101
        $this->assertTrue($argNodes[0]->isOptional());
102
        $this->assertEquals(array(), $argNodes[0]->getDefault());
103
        $this->assertFalse($argNodes[0]->isPassedByReference());
104
        $this->assertFalse($argNodes[0]->isVariadic());
105
106
        $this->assertEquals('arg_2', $argNodes[1]->getName());
107
        $this->assertEquals('ArrayAccess', $argNodes[1]->getTypeHint());
108
        $this->assertFalse($argNodes[1]->isOptional());
109
110
        $this->assertEquals('arg_3', $argNodes[2]->getName());
111
        $this->assertEquals('ArrayAccess', $argNodes[2]->getTypeHint());
112
        $this->assertTrue($argNodes[2]->isOptional());
113
        $this->assertNull($argNodes[2]->getDefault());
114
        $this->assertFalse($argNodes[2]->isPassedByReference());
115
        $this->assertFalse($argNodes[2]->isVariadic());
116
    }
117
118
    /**
119
     * @test
120
     * @requires PHP 5.4
121
     */
122
    public function it_properly_reads_methods_arguments_with_callable_types()
123
    {
124
        $class = new \ReflectionClass('Fixtures\Prophecy\WithCallableArgument');
125
126
        $mirror = new ClassMirror();
127
128
        $classNode = $mirror->reflect($class, array());
129
        $methodNode = $classNode->getMethod('methodWithArgs');
130
        $argNodes = $methodNode->getArguments();
131
132
        $this->assertCount(2, $argNodes);
133
134
        $this->assertEquals('arg_1', $argNodes[0]->getName());
135
        $this->assertEquals('callable', $argNodes[0]->getTypeHint());
136
        $this->assertFalse($argNodes[0]->isOptional());
137
        $this->assertFalse($argNodes[0]->isPassedByReference());
138
        $this->assertFalse($argNodes[0]->isVariadic());
139
140
        $this->assertEquals('arg_2', $argNodes[1]->getName());
141
        $this->assertEquals('callable', $argNodes[1]->getTypeHint());
142
        $this->assertTrue($argNodes[1]->isOptional());
143
        $this->assertNull($argNodes[1]->getDefault());
144
        $this->assertFalse($argNodes[1]->isPassedByReference());
145
        $this->assertFalse($argNodes[1]->isVariadic());
146
    }
147
148
    /**
149
     * @test
150
     * @requires PHP 5.6
151
     */
152
    public function it_properly_reads_methods_variadic_arguments()
153
    {
154
        $class = new \ReflectionClass('Fixtures\Prophecy\WithVariadicArgument');
155
156
        $mirror = new ClassMirror();
157
158
        $classNode = $mirror->reflect($class, array());
159
        $methodNode = $classNode->getMethod('methodWithArgs');
160
        $argNodes = $methodNode->getArguments();
161
162
        $this->assertCount(1, $argNodes);
163
164
        $this->assertEquals('args', $argNodes[0]->getName());
165
        $this->assertNull($argNodes[0]->getTypeHint());
166
        $this->assertFalse($argNodes[0]->isOptional());
167
        $this->assertFalse($argNodes[0]->isPassedByReference());
168
        $this->assertTrue($argNodes[0]->isVariadic());
169
170
        $methodNode = $classNode->getMethod('methodWithTypeHintedArgs');
171
        $argNodes = $methodNode->getArguments();
172
173
        $this->assertCount(1, $argNodes);
174
175
        $this->assertEquals('args', $argNodes[0]->getName());
176
        $this->assertEquals('array', $argNodes[0]->getTypeHint());
177
        $this->assertFalse($argNodes[0]->isOptional());
178
        $this->assertFalse($argNodes[0]->isPassedByReference());
179
        $this->assertTrue($argNodes[0]->isVariadic());
180
    }
181
182
    /**
183
     * @test
184
     */
185 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...
186
    {
187
        $class = new \ReflectionClass('Fixtures\Prophecy\WithReferences');
188
189
        $mirror = new ClassMirror();
190
191
        $classNode = $mirror->reflect($class, array());
192
193
        $this->assertTrue($classNode->hasMethod('methodWithReferenceArgument'));
194
195
        $argNodes = $classNode->getMethod('methodWithReferenceArgument')->getArguments();
196
197
        $this->assertCount(2, $argNodes);
198
199
        $this->assertTrue($argNodes[0]->isPassedByReference());
200
        $this->assertTrue($argNodes[1]->isPassedByReference());
201
    }
202
203
    /**
204
     * @test
205
     */
206 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...
207
    {
208
        $class = new \ReflectionClass('Fixtures\Prophecy\FinalClass');
209
210
        $mirror = new ClassMirror();
211
212
        $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...
213
214
        $mirror->reflect($class, array());
215
    }
216
217
    /**
218
     * @test
219
     */
220 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...
221
    {
222
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
223
224
        $mirror = new ClassMirror();
225
226
        $classNode = $mirror->reflect($class, array());
227
228
        $this->assertCount(0, $classNode->getMethods());
229
    }
230
231
    /**
232
     * @test
233
     */
234
    public function it_marks_final_methods_as_unextendable()
235
    {
236
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
237
238
        $mirror = new ClassMirror();
239
240
        $classNode = $mirror->reflect($class, array());
241
242
        $this->assertCount(1, $classNode->getUnextendableMethods());
243
        $this->assertFalse($classNode->isExtendable('finalImplementation'));
244
    }
245
246
    /**
247
     * @test
248
     */
249 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...
250
    {
251
        $class = new \ReflectionClass('Fixtures\Prophecy\EmptyInterface');
252
253
        $mirror = new ClassMirror();
254
255
        $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...
256
257
        $mirror->reflect($class, array());
258
    }
259
260
    /**
261
     * @test
262
     */
263
    public function it_reflects_all_interfaces_methods()
264
    {
265
        $mirror = new ClassMirror();
266
267
        $classNode = $mirror->reflect(null, array(
268
            new \ReflectionClass('Fixtures\Prophecy\Named'),
269
            new \ReflectionClass('Fixtures\Prophecy\ModifierInterface'),
270
        ));
271
272
        $this->assertEquals('stdClass', $classNode->getParentClass());
273
        $this->assertEquals(array(
274
            'Prophecy\Doubler\Generator\ReflectionInterface',
275
            'Fixtures\Prophecy\ModifierInterface',
276
            'Fixtures\Prophecy\Named',
277
        ), $classNode->getInterfaces());
278
279
        $this->assertCount(3, $classNode->getMethods());
280
        $this->assertTrue($classNode->hasMethod('getName'));
281
        $this->assertTrue($classNode->hasMethod('isAbstract'));
282
        $this->assertTrue($classNode->hasMethod('getVisibility'));
283
    }
284
285
    /**
286
     * @test
287
     */
288
    public function it_ignores_virtually_private_methods()
289
    {
290
        $class = new \ReflectionClass('Fixtures\Prophecy\WithVirtuallyPrivateMethod');
291
292
        $mirror = new ClassMirror();
293
294
        $classNode = $mirror->reflect($class, array());
295
296
        $this->assertCount(2, $classNode->getMethods());
297
        $this->assertTrue($classNode->hasMethod('isAbstract'));
298
        $this->assertTrue($classNode->hasMethod('__toString'));
299
        $this->assertFalse($classNode->hasMethod('_getName'));
300
    }
301
302
    /**
303
     * @test
304
     */
305 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...
306
    {
307
        $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalVirtuallyPrivateMethod');
308
309
        $mirror = new ClassMirror();
310
311
        $classNode = $mirror->reflect($class, array());
312
313
        $this->assertCount(0, $classNode->getMethods());
314
    }
315
316
    /**
317
     * @test
318
     * @requires PHP 7
319
     */
320
    public function it_reflects_return_typehints()
321
    {
322
        $class = new \ReflectionClass('Fixtures\Prophecy\WithReturnTypehints');
323
324
        $mirror = new ClassMirror();
325
326
        $classNode = $mirror->reflect($class, array());
327
328
        $this->assertCount(3, $classNode->getMethods());
329
        $this->assertTrue($classNode->hasMethod('getName'));
330
        $this->assertTrue($classNode->hasMethod('getSelf'));
331
        $this->assertTrue($classNode->hasMethod('getParent'));
332
333
        $this->assertEquals('string', $classNode->getMethod('getName')->getReturnType());
334
        $this->assertEquals('\Fixtures\Prophecy\WithReturnTypehints', $classNode->getMethod('getSelf')->getReturnType());
335
        $this->assertEquals('\Fixtures\Prophecy\EmptyClass', $classNode->getMethod('getParent')->getReturnType());
336
    }
337
338
    /**
339
     * @test
340
     */
341
    public function it_throws_an_exception_if_class_provided_in_interfaces_list()
342
    {
343
        $class = new \ReflectionClass('Fixtures\Prophecy\EmptyClass');
344
345
        $mirror = new ClassMirror();
346
347
        $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...
348
349
        $mirror->reflect(null, array($class));
350
    }
351
352
    /**
353
     * @test
354
     */
355
    public function it_throws_an_exception_if_not_reflection_provided_as_interface()
356
    {
357
        $mirror = new ClassMirror();
358
359
        $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...
360
361
        $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...
362
    }
363
364
    /**
365
     * @test
366
     */
367 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...
368
    {
369
        $mirror = new ClassMirror();
370
371
        $classNode = $mirror->reflect(new \ReflectionClass('ReflectionMethod'), array());
372
        $method = $classNode->getMethod('export');
373
        $arguments = $method->getArguments();
374
375
        $this->assertNull($arguments[0]->getTypeHint());
376
        $this->assertNull($arguments[1]->getTypeHint());
377
        $this->assertNull($arguments[2]->getTypeHint());
378
    }
379
380
    /**
381
     * @test
382
     */
383 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...
384
    {
385
        $mirror = new ClassMirror();
386
387
        $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
388
        $method = $classNode->getMethod('iHaveAStrangeTypeHintedArg');
389
        $arguments = $method->getArguments();
390
        $this->assertEquals('I\Simply\Am\Nonexistent', $arguments[0]->getTypeHint());
391
    }
392
393
    /**
394
     * @test
395
     */
396 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...
397
    {
398
        $mirror = new ClassMirror();
399
400
        $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
401
        $method = $classNode->getMethod('iHaveAnEvenStrangerTypeHintedArg');
402
        $arguments = $method->getArguments();
403
        $this->assertEquals('I\Simply\Am\Not', $arguments[0]->getTypeHint());
404
    }
405
406
    /**
407
     * @test
408
     */
409
    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...
410
    {
411
        // Use test doubles in this test, as arguments named ... in the Reflection API can only happen for internal classes
412
        $class = $this->prophesize('ReflectionClass');
413
        $method = $this->prophesize('ReflectionMethod');
414
        $parameter = $this->prophesize('ReflectionParameter');
415
416
        $class->getName()->willReturn('Custom\ClassName');
417
        $class->isInterface()->willReturn(false);
418
        $class->isFinal()->willReturn(false);
419
        $class->getMethods(\ReflectionMethod::IS_PUBLIC)->willReturn(array($method));
420
        $class->getMethods(\ReflectionMethod::IS_ABSTRACT)->willReturn(array());
421
422
        $method->getParameters()->willReturn(array($parameter));
423
        $method->getName()->willReturn('methodName');
424
        $method->isFinal()->willReturn(false);
425
        $method->isProtected()->willReturn(false);
426
        $method->isStatic()->willReturn(false);
427
        $method->returnsReference()->willReturn(false);
428
429
        if (version_compare(PHP_VERSION, '7.0', '>=')) {
430
            $method->hasReturnType()->willReturn(false);
431
        }
432
433
        $parameter->getName()->willReturn('...');
434
        $parameter->isDefaultValueAvailable()->willReturn(true);
435
        $parameter->getDefaultValue()->willReturn(null);
436
        $parameter->isPassedByReference()->willReturn(false);
437
        $parameter->getClass()->willReturn($class);
438
        if (version_compare(PHP_VERSION, '5.6', '>=')) {
439
            $parameter->isVariadic()->willReturn(false);
440
        }
441
442
        $mirror = new ClassMirror();
443
444
        $classNode = $mirror->reflect($class->reveal(), array());
445
446
        $methodNodes = $classNode->getMethods();
447
448
        $argumentNodes = $methodNodes['methodName']->getArguments();
449
        $argumentNode = $argumentNodes[0];
450
451
        $this->assertEquals('__dot_dot_dot__', $argumentNode->getName());
452
    }
453
}
454