comparison vendor/phpspec/prophecy/tests/Doubler/Generator/ClassMirrorTest.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
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_allowed_magic_methods()
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
171 /**
172 * @test
173 * @requires PHP 5.6
174 */
175 public function it_properly_reads_methods_typehinted_variadic_arguments()
176 {
177 if (defined('HHVM_VERSION_ID')) {
178 $this->markTestSkipped('HHVM does not support typehints on variadic arguments.');
179 }
180
181 $class = new \ReflectionClass('Fixtures\Prophecy\WithTypehintedVariadicArgument');
182
183 $mirror = new ClassMirror();
184
185 $classNode = $mirror->reflect($class, array());
186 $methodNode = $classNode->getMethod('methodWithTypeHintedArgs');
187 $argNodes = $methodNode->getArguments();
188
189 $this->assertCount(1, $argNodes);
190
191 $this->assertEquals('args', $argNodes[0]->getName());
192 $this->assertEquals('array', $argNodes[0]->getTypeHint());
193 $this->assertFalse($argNodes[0]->isOptional());
194 $this->assertFalse($argNodes[0]->isPassedByReference());
195 $this->assertTrue($argNodes[0]->isVariadic());
196 }
197
198 /**
199 * @test
200 */
201 public function it_marks_passed_by_reference_args_as_passed_by_reference()
202 {
203 $class = new \ReflectionClass('Fixtures\Prophecy\WithReferences');
204
205 $mirror = new ClassMirror();
206
207 $classNode = $mirror->reflect($class, array());
208
209 $this->assertTrue($classNode->hasMethod('methodWithReferenceArgument'));
210
211 $argNodes = $classNode->getMethod('methodWithReferenceArgument')->getArguments();
212
213 $this->assertCount(2, $argNodes);
214
215 $this->assertTrue($argNodes[0]->isPassedByReference());
216 $this->assertTrue($argNodes[1]->isPassedByReference());
217 }
218
219 /**
220 * @test
221 */
222 public function it_throws_an_exception_if_class_is_final()
223 {
224 $class = new \ReflectionClass('Fixtures\Prophecy\FinalClass');
225
226 $mirror = new ClassMirror();
227
228 $this->setExpectedException('Prophecy\Exception\Doubler\ClassMirrorException');
229
230 $mirror->reflect($class, array());
231 }
232
233 /**
234 * @test
235 */
236 public function it_ignores_final_methods()
237 {
238 $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
239
240 $mirror = new ClassMirror();
241
242 $classNode = $mirror->reflect($class, array());
243
244 $this->assertCount(0, $classNode->getMethods());
245 }
246
247 /**
248 * @test
249 */
250 public function it_marks_final_methods_as_unextendable()
251 {
252 $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod');
253
254 $mirror = new ClassMirror();
255
256 $classNode = $mirror->reflect($class, array());
257
258 $this->assertCount(1, $classNode->getUnextendableMethods());
259 $this->assertFalse($classNode->isExtendable('finalImplementation'));
260 }
261
262 /**
263 * @test
264 */
265 public function it_throws_an_exception_if_interface_provided_instead_of_class()
266 {
267 $class = new \ReflectionClass('Fixtures\Prophecy\EmptyInterface');
268
269 $mirror = new ClassMirror();
270
271 $this->setExpectedException('Prophecy\Exception\InvalidArgumentException');
272
273 $mirror->reflect($class, array());
274 }
275
276 /**
277 * @test
278 */
279 public function it_reflects_all_interfaces_methods()
280 {
281 $mirror = new ClassMirror();
282
283 $classNode = $mirror->reflect(null, array(
284 new \ReflectionClass('Fixtures\Prophecy\Named'),
285 new \ReflectionClass('Fixtures\Prophecy\ModifierInterface'),
286 ));
287
288 $this->assertEquals('stdClass', $classNode->getParentClass());
289 $this->assertEquals(array(
290 'Prophecy\Doubler\Generator\ReflectionInterface',
291 'Fixtures\Prophecy\ModifierInterface',
292 'Fixtures\Prophecy\Named',
293 ), $classNode->getInterfaces());
294
295 $this->assertCount(3, $classNode->getMethods());
296 $this->assertTrue($classNode->hasMethod('getName'));
297 $this->assertTrue($classNode->hasMethod('isAbstract'));
298 $this->assertTrue($classNode->hasMethod('getVisibility'));
299 }
300
301 /**
302 * @test
303 */
304 public function it_ignores_virtually_private_methods()
305 {
306 $class = new \ReflectionClass('Fixtures\Prophecy\WithVirtuallyPrivateMethod');
307
308 $mirror = new ClassMirror();
309
310 $classNode = $mirror->reflect($class, array());
311
312 $this->assertCount(2, $classNode->getMethods());
313 $this->assertTrue($classNode->hasMethod('isAbstract'));
314 $this->assertTrue($classNode->hasMethod('__toString'));
315 $this->assertFalse($classNode->hasMethod('_getName'));
316 }
317
318 /**
319 * @test
320 */
321 public function it_does_not_throw_exception_for_virtually_private_finals()
322 {
323 $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalVirtuallyPrivateMethod');
324
325 $mirror = new ClassMirror();
326
327 $classNode = $mirror->reflect($class, array());
328
329 $this->assertCount(0, $classNode->getMethods());
330 }
331
332 /**
333 * @test
334 * @requires PHP 7
335 */
336 public function it_reflects_return_typehints()
337 {
338 $class = new \ReflectionClass('Fixtures\Prophecy\WithReturnTypehints');
339
340 $mirror = new ClassMirror();
341
342 $classNode = $mirror->reflect($class, array());
343
344 $this->assertCount(3, $classNode->getMethods());
345 $this->assertTrue($classNode->hasMethod('getName'));
346 $this->assertTrue($classNode->hasMethod('getSelf'));
347 $this->assertTrue($classNode->hasMethod('getParent'));
348
349 $this->assertEquals('string', $classNode->getMethod('getName')->getReturnType());
350 $this->assertEquals('\Fixtures\Prophecy\WithReturnTypehints', $classNode->getMethod('getSelf')->getReturnType());
351 $this->assertEquals('\Fixtures\Prophecy\EmptyClass', $classNode->getMethod('getParent')->getReturnType());
352 }
353
354 /**
355 * @test
356 */
357 public function it_throws_an_exception_if_class_provided_in_interfaces_list()
358 {
359 $class = new \ReflectionClass('Fixtures\Prophecy\EmptyClass');
360
361 $mirror = new ClassMirror();
362
363 $this->setExpectedException('InvalidArgumentException');
364
365 $mirror->reflect(null, array($class));
366 }
367
368 /**
369 * @test
370 */
371 public function it_throws_an_exception_if_not_reflection_provided_as_interface()
372 {
373 $mirror = new ClassMirror();
374
375 $this->setExpectedException('InvalidArgumentException');
376
377 $mirror->reflect(null, array(null));
378 }
379
380 /**
381 * @test
382 */
383 public function it_doesnt_use_scalar_typehints()
384 {
385 $mirror = new ClassMirror();
386
387 $classNode = $mirror->reflect(new \ReflectionClass('ReflectionMethod'), array());
388 $method = $classNode->getMethod('export');
389 $arguments = $method->getArguments();
390
391 $this->assertNull($arguments[0]->getTypeHint());
392 $this->assertNull($arguments[1]->getTypeHint());
393 $this->assertNull($arguments[2]->getTypeHint());
394 }
395
396 /**
397 * @test
398 */
399 public function it_doesnt_fail_to_typehint_nonexistent_FQCN()
400 {
401 $mirror = new ClassMirror();
402
403 $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
404 $method = $classNode->getMethod('iHaveAStrangeTypeHintedArg');
405 $arguments = $method->getArguments();
406 $this->assertEquals('I\Simply\Am\Nonexistent', $arguments[0]->getTypeHint());
407 }
408
409 /**
410 * @test
411 */
412 public function it_doesnt_fail_to_typehint_nonexistent_RQCN()
413 {
414 $mirror = new ClassMirror();
415
416 $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array());
417 $method = $classNode->getMethod('iHaveAnEvenStrangerTypeHintedArg');
418 $arguments = $method->getArguments();
419 $this->assertEquals('I\Simply\Am\Not', $arguments[0]->getTypeHint());
420 }
421
422 /**
423 * @test
424 */
425 function it_changes_argument_names_if_they_are_varying()
426 {
427 // Use test doubles in this test, as arguments named ... in the Reflection API can only happen for internal classes
428 $class = $this->prophesize('ReflectionClass');
429 $method = $this->prophesize('ReflectionMethod');
430 $parameter = $this->prophesize('ReflectionParameter');
431
432 $class->getName()->willReturn('Custom\ClassName');
433 $class->isInterface()->willReturn(false);
434 $class->isFinal()->willReturn(false);
435 $class->getMethods(\ReflectionMethod::IS_PUBLIC)->willReturn(array($method));
436 $class->getMethods(\ReflectionMethod::IS_ABSTRACT)->willReturn(array());
437
438 $method->getParameters()->willReturn(array($parameter));
439 $method->getName()->willReturn('methodName');
440 $method->isFinal()->willReturn(false);
441 $method->isProtected()->willReturn(false);
442 $method->isStatic()->willReturn(false);
443 $method->returnsReference()->willReturn(false);
444
445 if (version_compare(PHP_VERSION, '7.0', '>=')) {
446 $method->hasReturnType()->willReturn(false);
447 }
448
449 $parameter->getName()->willReturn('...');
450 $parameter->isDefaultValueAvailable()->willReturn(true);
451 $parameter->getDefaultValue()->willReturn(null);
452 $parameter->isPassedByReference()->willReturn(false);
453 $parameter->getClass()->willReturn($class);
454 if (version_compare(PHP_VERSION, '5.6', '>=')) {
455 $parameter->isVariadic()->willReturn(false);
456 }
457
458 $mirror = new ClassMirror();
459
460 $classNode = $mirror->reflect($class->reveal(), array());
461
462 $methodNodes = $classNode->getMethods();
463
464 $argumentNodes = $methodNodes['methodName']->getArguments();
465 $argumentNode = $argumentNodes[0];
466
467 $this->assertEquals('__dot_dot_dot__', $argumentNode->getName());
468 }
469 }