diff --git a/src/phpDocumentor/Reflection/Php/Factory/Function_.php b/src/phpDocumentor/Reflection/Php/Factory/Function_.php index 3d524a11..ba00309d 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/Function_.php +++ b/src/phpDocumentor/Reflection/Php/Factory/Function_.php @@ -22,6 +22,7 @@ use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context; use PhpParser\Comment\Doc; +use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\Function_ as FunctionNode; /** @@ -59,9 +60,14 @@ protected function doCreate($object, StrategyContainer $strategies, Context $con $docBlock = $this->createDocBlock($strategies, $object->getDocComment(), $context); $returnType = null; - if ($object->returnType !== null) { + if ($object->getReturnType() !== null) { $typeResolver = new TypeResolver(); - $returnType = $typeResolver->resolve($object->returnType, $context); + if ($object->getReturnType() instanceof NullableType) { + $typeString = '?' . $object->getReturnType()->type; + } else { + $typeString = (string)$object->getReturnType(); + } + $returnType = $typeResolver->resolve($typeString, $context); } $function = new FunctionDescriptor($object->fqsen, $docBlock, new Location($object->getLine()), $returnType); diff --git a/src/phpDocumentor/Reflection/Php/Factory/Method.php b/src/phpDocumentor/Reflection/Php/Factory/Method.php index 77d0d1b8..65acc694 100644 --- a/src/phpDocumentor/Reflection/Php/Factory/Method.php +++ b/src/phpDocumentor/Reflection/Php/Factory/Method.php @@ -20,6 +20,7 @@ use phpDocumentor\Reflection\TypeResolver; use phpDocumentor\Reflection\Types\Context; use phpDocumentor\Reflection\Types\Mixed_; +use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\ClassMethod; /** @@ -51,9 +52,14 @@ protected function doCreate($object, StrategyContainer $strategies, Context $con $docBlock = $this->createDocBlock($strategies, $object->getDocComment(), $context); $returnType = null; - if ($object->returnType !== null) { + if ($object->getReturnType() !== null) { $typeResolver = new TypeResolver(); - $returnType = $typeResolver->resolve($object->returnType, $context); + if ($object->getReturnType() instanceof NullableType) { + $typeString = '?' . $object->getReturnType()->type; + } else { + $typeString = (string)$object->getReturnType(); + } + $returnType = $typeResolver->resolve($typeString, $context); } $method = new MethodDescriptor( diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php index e225c1b9..efccbc6f 100644 --- a/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php +++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/Function_Test.php @@ -20,7 +20,10 @@ use Mockery as m; use phpDocumentor\Reflection\Php\StrategyContainer; use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\Integer; +use phpDocumentor\Reflection\Types\Nullable; use PhpParser\Comment\Doc; +use PhpParser\Node\NullableType; /** * Test case for \phpDocumentor\Reflection\Php\Factory\Function_ @@ -55,6 +58,7 @@ public function testCreateWithoutParameters() $functionMock->params = []; $functionMock->shouldReceive('getDocComment')->andReturnNull(); $functionMock->shouldReceive('getLine')->andReturn(1); + $functionMock->shouldReceive('getReturnType')->andReturnNull(); $containerMock = m::mock(StrategyContainer::class); $containerMock->shouldReceive('findMatching')->never(); @@ -75,6 +79,7 @@ public function testCreateWithParameters() $functionMock->params = array('param1'); $functionMock->shouldReceive('getDocComment')->andReturnNull(); $functionMock->shouldReceive('getLine')->andReturn(1); + $functionMock->shouldReceive('getReturnType')->andReturnNull(); $containerMock = m::mock(StrategyContainer::class); $containerMock->shouldReceive('findMatching->create') @@ -88,6 +93,50 @@ public function testCreateWithParameters() $this->assertEquals('\SomeSpace::function()', (string)$function->getFqsen()); } + /** + * @covers ::create + */ + public function testReturnTypeResolving() + { + $functionMock = m::mock(\PhpParser\Node\Stmt\Function_::class); + $functionMock->fqsen = new Fqsen('\SomeSpace::function()'); + $functionMock->params = []; + $functionMock->shouldReceive('getDocComment')->andReturnNull(); + $functionMock->shouldReceive('getLine')->andReturn(1); + $functionMock->shouldReceive('getReturnType')->times(3)->andReturn('int'); + + $containerMock = m::mock(StrategyContainer::class); + $containerMock->shouldReceive('findMatching')->never(); + + + /** @var FunctionDescriptor $function */ + $function = $this->fixture->create($functionMock, $containerMock); + + $this->assertEquals(new Integer(), $function->getReturnType()); + } + + /** + * @covers ::create + */ + public function testReturnTypeNullableResolving() + { + $functionMock = m::mock(\PhpParser\Node\Stmt\Function_::class); + $functionMock->fqsen = new Fqsen('\SomeSpace::function()'); + $functionMock->params = []; + $functionMock->shouldReceive('getDocComment')->andReturnNull(); + $functionMock->shouldReceive('getLine')->andReturn(1); + $functionMock->shouldReceive('getReturnType')->times(3)->andReturn(new NullableType('int')); + + $containerMock = m::mock(StrategyContainer::class); + $containerMock->shouldReceive('findMatching')->never(); + + + /** @var FunctionDescriptor $method */ + $function = $this->fixture->create($functionMock, $containerMock); + + $this->assertEquals(new Nullable(new Integer()), $function->getReturnType()); + } + /** * @covers ::create */ @@ -99,6 +148,7 @@ public function testCreateWithDocBlock() $functionMock->params = []; $functionMock->shouldReceive('getDocComment')->andReturn($doc); $functionMock->shouldReceive('getLine')->andReturn(1); + $functionMock->shouldReceive('getReturnType')->andReturnNull(); $docBlock = new DocBlockDescriptor(''); diff --git a/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php b/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php index 7979b1e3..8af35342 100644 --- a/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php +++ b/tests/unit/phpDocumentor/Reflection/Php/Factory/MethodTest.php @@ -19,7 +19,10 @@ use Mockery as m; use phpDocumentor\Reflection\Php\StrategyContainer; use phpDocumentor\Reflection\Types\Context; +use phpDocumentor\Reflection\Types\Integer; +use phpDocumentor\Reflection\Types\Nullable; use PhpParser\Comment\Doc; +use PhpParser\Node\NullableType; use PhpParser\Node\Stmt\ClassMethod; /** @@ -53,6 +56,7 @@ public function testCreateWithoutParameters() $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(false); $classMethodMock->shouldReceive('isProtected')->once()->andReturn(false); $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull(); + $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null); $containerMock = m::mock(StrategyContainer::class); $containerMock->shouldReceive('findMatching')->never(); @@ -71,6 +75,7 @@ public function testCreateProtectedMethod() $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(false); $classMethodMock->shouldReceive('isProtected')->once()->andReturn(true); $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull(); + $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null); $containerMock = m::mock(StrategyContainer::class); $containerMock->shouldReceive('findMatching')->never(); @@ -91,6 +96,7 @@ public function testCreateWithParameters() $classMethodMock->params = array('param1'); $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(true); $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull(); + $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null); $containerMock = m::mock(StrategyContainer::class); $containerMock->shouldReceive('findMatching->create') @@ -108,6 +114,48 @@ public function testCreateWithParameters() $this->assertEquals('private', (string)$method->getVisibility()); } + /** + * @covers ::create + */ + public function testReturnTypeResolving() + { + $classMethodMock = $this->buildClassMethodMock(); + $classMethodMock->params = []; + $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(true); + $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull(); + $classMethodMock->shouldReceive('getReturnType')->times(3)->andReturn('int'); + + $containerMock = m::mock(StrategyContainer::class); + $containerMock->shouldReceive('findMatching')->never(); + + + /** @var MethodDescriptor $method */ + $method = $this->fixture->create($classMethodMock, $containerMock); + + $this->assertEquals(new Integer(), $method->getReturnType()); + } + + /** + * @covers ::create + */ + public function testReturnTypeNullableResolving() + { + $classMethodMock = $this->buildClassMethodMock(); + $classMethodMock->params = []; + $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(true); + $classMethodMock->shouldReceive('getDocComment')->once()->andReturnNull(); + $classMethodMock->shouldReceive('getReturnType')->times(3)->andReturn(new NullableType('int')); + + $containerMock = m::mock(StrategyContainer::class); + $containerMock->shouldReceive('findMatching')->never(); + + + /** @var MethodDescriptor $method */ + $method = $this->fixture->create($classMethodMock, $containerMock); + + $this->assertEquals(new Nullable(new Integer()), $method->getReturnType()); + } + /** * @covers ::create */ @@ -118,6 +166,7 @@ public function testCreateWithDocBlock() $classMethodMock->params = array(); $classMethodMock->shouldReceive('isPrivate')->once()->andReturn(true); $classMethodMock->shouldReceive('getDocComment')->andReturn($doc); + $classMethodMock->shouldReceive('getReturnType')->once()->andReturn(null); $docBlock = new DocBlockDescriptor('');