Chris@0: reflect($class, array()); Chris@0: Chris@0: $this->assertCount(7, $node->getMethods()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_reflects_protected_abstract_methods() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithProtectedAbstractMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertEquals('Fixtures\Prophecy\WithProtectedAbstractMethod', $classNode->getParentClass()); Chris@0: Chris@0: $methodNodes = $classNode->getMethods(); Chris@0: $this->assertCount(1, $methodNodes); Chris@0: Chris@0: $this->assertEquals('protected', $methodNodes['innerDetail']->getVisibility()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_reflects_public_static_methods() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithStaticMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertEquals('Fixtures\Prophecy\WithStaticMethod', $classNode->getParentClass()); Chris@0: Chris@0: $methodNodes = $classNode->getMethods(); Chris@0: $this->assertCount(1, $methodNodes); Chris@0: Chris@0: $this->assertTrue($methodNodes['innerDetail']->isStatic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_marks_required_args_without_types_as_not_optional() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: $methodNode = $classNode->getMethod('methodWithoutTypeHints'); Chris@0: $argNodes = $methodNode->getArguments(); Chris@0: Chris@0: $this->assertCount(1, $argNodes); Chris@0: Chris@0: $this->assertEquals('arg', $argNodes[0]->getName()); Chris@0: $this->assertNull($argNodes[0]->getTypeHint()); Chris@0: $this->assertFalse($argNodes[0]->isOptional()); Chris@0: $this->assertNull($argNodes[0]->getDefault()); Chris@0: $this->assertFalse($argNodes[0]->isPassedByReference()); Chris@0: $this->assertFalse($argNodes[0]->isVariadic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_properly_reads_methods_arguments_with_types() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithArguments'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: $methodNode = $classNode->getMethod('methodWithArgs'); Chris@0: $argNodes = $methodNode->getArguments(); Chris@0: Chris@0: $this->assertCount(3, $argNodes); Chris@0: Chris@0: $this->assertEquals('arg_1', $argNodes[0]->getName()); Chris@0: $this->assertEquals('array', $argNodes[0]->getTypeHint()); Chris@0: $this->assertTrue($argNodes[0]->isOptional()); Chris@0: $this->assertEquals(array(), $argNodes[0]->getDefault()); Chris@0: $this->assertFalse($argNodes[0]->isPassedByReference()); Chris@0: $this->assertFalse($argNodes[0]->isVariadic()); Chris@0: Chris@0: $this->assertEquals('arg_2', $argNodes[1]->getName()); Chris@0: $this->assertEquals('ArrayAccess', $argNodes[1]->getTypeHint()); Chris@0: $this->assertFalse($argNodes[1]->isOptional()); Chris@0: Chris@0: $this->assertEquals('arg_3', $argNodes[2]->getName()); Chris@0: $this->assertEquals('ArrayAccess', $argNodes[2]->getTypeHint()); Chris@0: $this->assertTrue($argNodes[2]->isOptional()); Chris@0: $this->assertNull($argNodes[2]->getDefault()); Chris@0: $this->assertFalse($argNodes[2]->isPassedByReference()); Chris@0: $this->assertFalse($argNodes[2]->isVariadic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: * @requires PHP 5.4 Chris@0: */ Chris@0: public function it_properly_reads_methods_arguments_with_callable_types() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithCallableArgument'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: $methodNode = $classNode->getMethod('methodWithArgs'); Chris@0: $argNodes = $methodNode->getArguments(); Chris@0: Chris@0: $this->assertCount(2, $argNodes); Chris@0: Chris@0: $this->assertEquals('arg_1', $argNodes[0]->getName()); Chris@0: $this->assertEquals('callable', $argNodes[0]->getTypeHint()); Chris@0: $this->assertFalse($argNodes[0]->isOptional()); Chris@0: $this->assertFalse($argNodes[0]->isPassedByReference()); Chris@0: $this->assertFalse($argNodes[0]->isVariadic()); Chris@0: Chris@0: $this->assertEquals('arg_2', $argNodes[1]->getName()); Chris@0: $this->assertEquals('callable', $argNodes[1]->getTypeHint()); Chris@0: $this->assertTrue($argNodes[1]->isOptional()); Chris@0: $this->assertNull($argNodes[1]->getDefault()); Chris@0: $this->assertFalse($argNodes[1]->isPassedByReference()); Chris@0: $this->assertFalse($argNodes[1]->isVariadic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: * @requires PHP 5.6 Chris@0: */ Chris@0: public function it_properly_reads_methods_variadic_arguments() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithVariadicArgument'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: $methodNode = $classNode->getMethod('methodWithArgs'); Chris@0: $argNodes = $methodNode->getArguments(); Chris@0: Chris@0: $this->assertCount(1, $argNodes); Chris@0: Chris@0: $this->assertEquals('args', $argNodes[0]->getName()); Chris@0: $this->assertNull($argNodes[0]->getTypeHint()); Chris@0: $this->assertFalse($argNodes[0]->isOptional()); Chris@0: $this->assertFalse($argNodes[0]->isPassedByReference()); Chris@0: $this->assertTrue($argNodes[0]->isVariadic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: * @requires PHP 5.6 Chris@0: */ Chris@0: public function it_properly_reads_methods_typehinted_variadic_arguments() Chris@0: { Chris@0: if (defined('HHVM_VERSION_ID')) { Chris@0: $this->markTestSkipped('HHVM does not support typehints on variadic arguments.'); Chris@0: } Chris@0: Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithTypehintedVariadicArgument'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: $methodNode = $classNode->getMethod('methodWithTypeHintedArgs'); Chris@0: $argNodes = $methodNode->getArguments(); Chris@0: Chris@0: $this->assertCount(1, $argNodes); Chris@0: Chris@0: $this->assertEquals('args', $argNodes[0]->getName()); Chris@0: $this->assertEquals('array', $argNodes[0]->getTypeHint()); Chris@0: $this->assertFalse($argNodes[0]->isOptional()); Chris@0: $this->assertFalse($argNodes[0]->isPassedByReference()); Chris@0: $this->assertTrue($argNodes[0]->isVariadic()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_marks_passed_by_reference_args_as_passed_by_reference() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithReferences'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertTrue($classNode->hasMethod('methodWithReferenceArgument')); Chris@0: Chris@0: $argNodes = $classNode->getMethod('methodWithReferenceArgument')->getArguments(); Chris@0: Chris@0: $this->assertCount(2, $argNodes); Chris@0: Chris@0: $this->assertTrue($argNodes[0]->isPassedByReference()); Chris@0: $this->assertTrue($argNodes[1]->isPassedByReference()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_throws_an_exception_if_class_is_final() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\FinalClass'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $this->setExpectedException('Prophecy\Exception\Doubler\ClassMirrorException'); Chris@0: Chris@0: $mirror->reflect($class, array()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_ignores_final_methods() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertCount(0, $classNode->getMethods()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_marks_final_methods_as_unextendable() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertCount(1, $classNode->getUnextendableMethods()); Chris@0: $this->assertFalse($classNode->isExtendable('finalImplementation')); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_throws_an_exception_if_interface_provided_instead_of_class() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\EmptyInterface'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $this->setExpectedException('Prophecy\Exception\InvalidArgumentException'); Chris@0: Chris@0: $mirror->reflect($class, array()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_reflects_all_interfaces_methods() Chris@0: { Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect(null, array( Chris@0: new \ReflectionClass('Fixtures\Prophecy\Named'), Chris@0: new \ReflectionClass('Fixtures\Prophecy\ModifierInterface'), Chris@0: )); Chris@0: Chris@0: $this->assertEquals('stdClass', $classNode->getParentClass()); Chris@0: $this->assertEquals(array( Chris@0: 'Prophecy\Doubler\Generator\ReflectionInterface', Chris@0: 'Fixtures\Prophecy\ModifierInterface', Chris@0: 'Fixtures\Prophecy\Named', Chris@0: ), $classNode->getInterfaces()); Chris@0: Chris@0: $this->assertCount(3, $classNode->getMethods()); Chris@0: $this->assertTrue($classNode->hasMethod('getName')); Chris@0: $this->assertTrue($classNode->hasMethod('isAbstract')); Chris@0: $this->assertTrue($classNode->hasMethod('getVisibility')); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_ignores_virtually_private_methods() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithVirtuallyPrivateMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertCount(2, $classNode->getMethods()); Chris@0: $this->assertTrue($classNode->hasMethod('isAbstract')); Chris@0: $this->assertTrue($classNode->hasMethod('__toString')); Chris@0: $this->assertFalse($classNode->hasMethod('_getName')); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_does_not_throw_exception_for_virtually_private_finals() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithFinalVirtuallyPrivateMethod'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertCount(0, $classNode->getMethods()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: * @requires PHP 7 Chris@0: */ Chris@0: public function it_reflects_return_typehints() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\WithReturnTypehints'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class, array()); Chris@0: Chris@0: $this->assertCount(3, $classNode->getMethods()); Chris@0: $this->assertTrue($classNode->hasMethod('getName')); Chris@0: $this->assertTrue($classNode->hasMethod('getSelf')); Chris@0: $this->assertTrue($classNode->hasMethod('getParent')); Chris@0: Chris@0: $this->assertEquals('string', $classNode->getMethod('getName')->getReturnType()); Chris@0: $this->assertEquals('\Fixtures\Prophecy\WithReturnTypehints', $classNode->getMethod('getSelf')->getReturnType()); Chris@0: $this->assertEquals('\Fixtures\Prophecy\EmptyClass', $classNode->getMethod('getParent')->getReturnType()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_throws_an_exception_if_class_provided_in_interfaces_list() Chris@0: { Chris@0: $class = new \ReflectionClass('Fixtures\Prophecy\EmptyClass'); Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $this->setExpectedException('InvalidArgumentException'); Chris@0: Chris@0: $mirror->reflect(null, array($class)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_throws_an_exception_if_not_reflection_provided_as_interface() Chris@0: { Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $this->setExpectedException('InvalidArgumentException'); Chris@0: Chris@0: $mirror->reflect(null, array(null)); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_doesnt_use_scalar_typehints() Chris@0: { Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect(new \ReflectionClass('ReflectionMethod'), array()); Chris@0: $method = $classNode->getMethod('export'); Chris@0: $arguments = $method->getArguments(); Chris@0: Chris@0: $this->assertNull($arguments[0]->getTypeHint()); Chris@0: $this->assertNull($arguments[1]->getTypeHint()); Chris@0: $this->assertNull($arguments[2]->getTypeHint()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_doesnt_fail_to_typehint_nonexistent_FQCN() Chris@0: { Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array()); Chris@0: $method = $classNode->getMethod('iHaveAStrangeTypeHintedArg'); Chris@0: $arguments = $method->getArguments(); Chris@0: $this->assertEquals('I\Simply\Am\Nonexistent', $arguments[0]->getTypeHint()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: public function it_doesnt_fail_to_typehint_nonexistent_RQCN() Chris@0: { Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect(new \ReflectionClass('Fixtures\Prophecy\OptionalDepsClass'), array()); Chris@0: $method = $classNode->getMethod('iHaveAnEvenStrangerTypeHintedArg'); Chris@0: $arguments = $method->getArguments(); Chris@0: $this->assertEquals('I\Simply\Am\Not', $arguments[0]->getTypeHint()); Chris@0: } Chris@0: Chris@0: /** Chris@0: * @test Chris@0: */ Chris@0: function it_changes_argument_names_if_they_are_varying() Chris@0: { Chris@0: // Use test doubles in this test, as arguments named ... in the Reflection API can only happen for internal classes Chris@0: $class = $this->prophesize('ReflectionClass'); Chris@0: $method = $this->prophesize('ReflectionMethod'); Chris@0: $parameter = $this->prophesize('ReflectionParameter'); Chris@0: Chris@0: $class->getName()->willReturn('Custom\ClassName'); Chris@0: $class->isInterface()->willReturn(false); Chris@0: $class->isFinal()->willReturn(false); Chris@0: $class->getMethods(\ReflectionMethod::IS_PUBLIC)->willReturn(array($method)); Chris@0: $class->getMethods(\ReflectionMethod::IS_ABSTRACT)->willReturn(array()); Chris@0: Chris@0: $method->getParameters()->willReturn(array($parameter)); Chris@0: $method->getName()->willReturn('methodName'); Chris@0: $method->isFinal()->willReturn(false); Chris@0: $method->isProtected()->willReturn(false); Chris@0: $method->isStatic()->willReturn(false); Chris@0: $method->returnsReference()->willReturn(false); Chris@0: Chris@0: if (version_compare(PHP_VERSION, '7.0', '>=')) { Chris@0: $method->hasReturnType()->willReturn(false); Chris@0: } Chris@0: Chris@0: $parameter->getName()->willReturn('...'); Chris@0: $parameter->isDefaultValueAvailable()->willReturn(true); Chris@0: $parameter->getDefaultValue()->willReturn(null); Chris@0: $parameter->isPassedByReference()->willReturn(false); Chris@0: $parameter->getClass()->willReturn($class); Chris@0: if (version_compare(PHP_VERSION, '5.6', '>=')) { Chris@0: $parameter->isVariadic()->willReturn(false); Chris@0: } Chris@0: Chris@0: $mirror = new ClassMirror(); Chris@0: Chris@0: $classNode = $mirror->reflect($class->reveal(), array()); Chris@0: Chris@0: $methodNodes = $classNode->getMethods(); Chris@0: Chris@0: $argumentNodes = $methodNodes['methodName']->getArguments(); Chris@0: $argumentNode = $argumentNodes[0]; Chris@0: Chris@0: $this->assertEquals('__dot_dot_dot__', $argumentNode->getName()); Chris@0: } Chris@0: }