From 443d86e18e83a62541621f00356c353fcc02f463 Mon Sep 17 00:00:00 2001 From: Lars Moelleken Date: Mon, 17 Aug 2020 23:44:09 +0200 Subject: [PATCH] Param: fix phpdoc with reference hint fix issue #251 --- src/DocBlock/Tags/Param.php | 50 +++++++++++++--- tests/unit/DocBlock/Tags/ParamTest.php | 82 ++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 9 deletions(-) diff --git a/src/DocBlock/Tags/Param.php b/src/DocBlock/Tags/Param.php index 2e5daf7..1eb1de0 100644 --- a/src/DocBlock/Tags/Param.php +++ b/src/DocBlock/Tags/Param.php @@ -38,17 +38,22 @@ final class Param extends TagWithType implements Factory\StaticMethod /** @var bool determines whether this is a variadic argument */ private $isVariadic; + /** @var bool determines whether this is passed by reference */ + private $isReference; + public function __construct( ?string $variableName, ?Type $type = null, bool $isVariadic = false, - ?Description $description = null + ?Description $description = null, + bool $isReference = false ) { $this->name = 'param'; $this->variableName = $variableName; $this->type = $type; $this->isVariadic = $isVariadic; $this->description = $description; + $this->isReference = $isReference; } public static function create( @@ -67,6 +72,7 @@ final class Param extends TagWithType implements Factory\StaticMethod $parts = Utils::pregSplit('/(\s+)/Su', $body, 2, PREG_SPLIT_DELIM_CAPTURE); $variableName = ''; $isVariadic = false; + $isReference = false; // if the first item that is encountered is not a variable; it is a type if ($firstPart && $firstPart[0] !== '$') { @@ -76,26 +82,43 @@ final class Param extends TagWithType implements Factory\StaticMethod array_unshift($parts, $firstPart); } - // if the next item starts with a $ or ...$ it must be the variable name - if (isset($parts[0]) && (strpos($parts[0], '$') === 0 || strpos($parts[0], '...$') === 0)) { + // if the next item starts with a $ or ...$ or &$ or &...$ it must be the variable name + if ( + isset($parts[0]) + && + ( + strpos($parts[0], '$') === 0 + || + strpos($parts[0], '...$') === 0 + || + strpos($parts[0], '&$') === 0 + || + strpos($parts[0], '&...$') === 0 + ) + ) { $variableName = array_shift($parts); array_shift($parts); Assert::notNull($variableName); - if (strpos($variableName, '...') === 0) { - $isVariadic = true; - $variableName = substr($variableName, 3); - } - if (strpos($variableName, '$') === 0) { $variableName = substr($variableName, 1); + } elseif(strpos($variableName, '&$') === 0) { + $isReference = true; + $variableName = substr($variableName, 2); + } elseif (strpos($variableName, '...$') === 0) { + $isVariadic = true; + $variableName = substr($variableName, 4); + } elseif (strpos($variableName, '&...$') === 0) { + $isVariadic = true; + $isReference = true; + $variableName = substr($variableName, 5); } } $description = $descriptionFactory->create(implode('', $parts), $context); - return new static($variableName, $type, $isVariadic, $description); + return new static($variableName, $type, $isVariadic, $description, $isReference); } /** @@ -114,12 +137,21 @@ final class Param extends TagWithType implements Factory\StaticMethod return $this->isVariadic; } + /** + * Returns whether this tag is passed by reference. + */ + public function isReference() : bool + { + return $this->isReference; + } + /** * Returns a string representation for this tag. */ public function __toString() : string { return ($this->type ? $this->type . ' ' : '') + . ($this->isReference() ? '&' : '') . ($this->isVariadic() ? '...' : '') . ($this->variableName !== null ? '$' . $this->variableName : '') . ($this->description ? ' ' . $this->description : ''); diff --git a/tests/unit/DocBlock/Tags/ParamTest.php b/tests/unit/DocBlock/Tags/ParamTest.php index 9e4513a..c4651ea 100644 --- a/tests/unit/DocBlock/Tags/ParamTest.php +++ b/tests/unit/DocBlock/Tags/ParamTest.php @@ -174,12 +174,94 @@ class ParamTest extends TestCase $description = new Description('My Description'); $descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description); + $fixture = Param::create('string $myParameter My Description', $typeResolver, $descriptionFactory, $context); + + $this->assertSame('string $myParameter My Description', (string) $fixture); + $this->assertSame('myParameter', $fixture->getVariableName()); + $this->assertInstanceOf(String_::class, $fixture->getType()); + $this->assertFalse($fixture->isVariadic()); + $this->assertFalse($fixture->isReference()); + $this->assertSame($description, $fixture->getDescription()); + } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Param:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::create + */ + public function testFactoryMethodWithVariadic() : void + { + $typeResolver = new TypeResolver(); + $descriptionFactory = m::mock(DescriptionFactory::class); + $context = new Context(''); + + $description = new Description('My Description'); + $descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description); + $fixture = Param::create('string ...$myParameter My Description', $typeResolver, $descriptionFactory, $context); $this->assertSame('string ...$myParameter My Description', (string) $fixture); $this->assertSame('myParameter', $fixture->getVariableName()); $this->assertInstanceOf(String_::class, $fixture->getType()); $this->assertTrue($fixture->isVariadic()); + $this->assertFalse($fixture->isReference()); + $this->assertSame($description, $fixture->getDescription()); + } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Param:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::create + */ + public function testFactoryMethodWithReference() : void + { + $typeResolver = new TypeResolver(); + $descriptionFactory = m::mock(DescriptionFactory::class); + $context = new Context(''); + + $description = new Description('My Description'); + $descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description); + + $fixture = Param::create('string &$myParameter My Description', $typeResolver, $descriptionFactory, $context); + + $this->assertSame('string &$myParameter My Description', (string) $fixture); + $this->assertSame('myParameter', $fixture->getVariableName()); + $this->assertInstanceOf(String_::class, $fixture->getType()); + $this->assertFalse($fixture->isVariadic()); + $this->assertTrue($fixture->isReference()); + $this->assertSame($description, $fixture->getDescription()); + } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Param:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::create + */ + public function testFactoryMethodWithVariadicReference() : void + { + $typeResolver = new TypeResolver(); + $descriptionFactory = m::mock(DescriptionFactory::class); + $context = new Context(''); + + $description = new Description('My Description'); + $descriptionFactory->shouldReceive('create')->with('My Description', $context)->andReturn($description); + + $fixture = Param::create('string &...$myParameter My Description', $typeResolver, $descriptionFactory, $context); + + $this->assertSame('string &...$myParameter My Description', (string) $fixture); + $this->assertSame('myParameter', $fixture->getVariableName()); + $this->assertInstanceOf(String_::class, $fixture->getType()); + $this->assertTrue($fixture->isVariadic()); + $this->assertTrue($fixture->isReference()); $this->assertSame($description, $fixture->getDescription()); }