From 29ed9ae60d24ae09245a5283e8a61c0e64491d19 Mon Sep 17 00:00:00 2001 From: Fabien Villepinte Date: Thu, 31 Mar 2022 13:22:11 +0000 Subject: [PATCH] Support reference in method tag --- src/DocBlock/Tags/Method.php | 45 ++++++++++++++++++------- tests/unit/DocBlock/Tags/MethodTest.php | 42 +++++++++++++++++++++++ 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/src/DocBlock/Tags/Method.php b/src/DocBlock/Tags/Method.php index f08bfff..12996a4 100644 --- a/src/DocBlock/Tags/Method.php +++ b/src/DocBlock/Tags/Method.php @@ -57,6 +57,9 @@ final class Method extends BaseTag implements Factory\StaticMethod /** @var Type */ private $returnType; + /** @var bool */ + private $returnsReference; + /** * @param array> $arguments * @phpstan-param array $arguments @@ -66,7 +69,8 @@ final class Method extends BaseTag implements Factory\StaticMethod array $arguments = [], ?Type $returnType = null, bool $static = false, - ?Description $description = null + ?Description $description = null, + bool $returnsReference = false ) { Assert::stringNotEmpty($methodName); @@ -74,11 +78,12 @@ final class Method extends BaseTag implements Factory\StaticMethod $returnType = new Void_(); } - $this->methodName = $methodName; - $this->arguments = $this->filterArguments($arguments); - $this->returnType = $returnType; - $this->isStatic = $static; - $this->description = $description; + $this->methodName = $methodName; + $this->arguments = $this->filterArguments($arguments); + $this->returnType = $returnType; + $this->isStatic = $static; + $this->description = $description; + $this->returnsReference = $returnsReference; } public static function create( @@ -95,11 +100,13 @@ final class Method extends BaseTag implements Factory\StaticMethod // 2. optionally the keyword "static" followed by whitespace // 3. optionally a word with underscores followed by whitespace : as // type for the return value - // 4. then optionally a word with underscores followed by () and + // 4. optionally an ampersand followed or not by whitespace : as + // a reference + // 5. then optionally a word with underscores followed by () and // whitespace : as method name as used by phpDocumentor - // 5. then a word with underscores, followed by ( and any character + // 6. then a word with underscores, followed by ( and any character // until a ) and whitespace : as method name with signature - // 6. any remaining text : as description + // 7. any remaining text : as description if ( !preg_match( '/^ @@ -122,6 +129,11 @@ final class Method extends BaseTag implements Factory\StaticMethod ) \s+ )? + # Returns reference + (?: + (&) + \s* + )? # Method name ([\w_]+) # Arguments @@ -139,7 +151,7 @@ final class Method extends BaseTag implements Factory\StaticMethod return null; } - [, $static, $returnType, $methodName, $argumentLines, $description] = $matches; + [, $static, $returnType, $returnsReference, $methodName, $argumentLines, $description] = $matches; $static = $static === 'static'; @@ -147,6 +159,8 @@ final class Method extends BaseTag implements Factory\StaticMethod $returnType = 'void'; } + $returnsReference = $returnsReference === '&'; + $returnType = $typeResolver->resolve($returnType, $context); $description = $descriptionFactory->create($description, $context); @@ -172,7 +186,7 @@ final class Method extends BaseTag implements Factory\StaticMethod } } - return new static($methodName, $arguments, $returnType, $static, $description); + return new static($methodName, $arguments, $returnType, $static, $description, $returnsReference); } /** @@ -207,6 +221,11 @@ final class Method extends BaseTag implements Factory\StaticMethod return $this->returnType; } + public function returnsReference(): bool + { + return $this->returnsReference; + } + public function __toString(): string { $arguments = []; @@ -228,9 +247,11 @@ final class Method extends BaseTag implements Factory\StaticMethod $methodName = $this->methodName; + $reference = $this->returnsReference ? '&' : ''; + return $static . ($returnType !== '' ? ($static !== '' ? ' ' : '') . $returnType : '') - . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $methodName : '') + . ($methodName !== '' ? ($static !== '' || $returnType !== '' ? ' ' : '') . $reference . $methodName : '') . $argumentStr . ($description !== '' ? ' ' . $description : ''); } diff --git a/tests/unit/DocBlock/Tags/MethodTest.php b/tests/unit/DocBlock/Tags/MethodTest.php index 1f068db..dfc0032 100644 --- a/tests/unit/DocBlock/Tags/MethodTest.php +++ b/tests/unit/DocBlock/Tags/MethodTest.php @@ -339,6 +339,7 @@ class MethodTest extends TestCase $this->assertEquals($expectedArguments, $fixture->getArguments()); $this->assertInstanceOf(Void_::class, $fixture->getReturnType()); $this->assertSame($description, $fixture->getDescription()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -370,6 +371,7 @@ class MethodTest extends TestCase $this->assertSame('static $this myMethod()', (string) $fixture); $this->assertSame('myMethod', $fixture->getMethodName()); $this->assertInstanceOf(This::class, $fixture->getReturnType()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -401,6 +403,7 @@ class MethodTest extends TestCase $this->assertSame('void myVeryLongMethodName(mixed $node)', (string) $fixture); $this->assertSame('myVeryLongMethodName', $fixture->getMethodName()); $this->assertInstanceOf(Void_::class, $fixture->getReturnType()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -542,6 +545,7 @@ class MethodTest extends TestCase $this->assertEquals([], $fixture->getArguments()); $this->assertInstanceOf(Void_::class, $fixture->getReturnType()); $this->assertSame($description, $fixture->getDescription()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -578,6 +582,7 @@ class MethodTest extends TestCase $this->assertEquals([], $fixture->getArguments()); $this->assertInstanceOf(Void_::class, $fixture->getReturnType()); $this->assertSame($description, $fixture->getDescription()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -613,6 +618,7 @@ class MethodTest extends TestCase $this->assertEquals([], $fixture->getArguments()); $this->assertInstanceOf(Void_::class, $fixture->getReturnType()); $this->assertSame($description, $fixture->getDescription()); + $this->assertFalse($fixture->returnsReference()); } /** @@ -656,4 +662,40 @@ class MethodTest extends TestCase $fixture->getReturnType() ); } + + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Method:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\TypeResolver + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\Fqsen + * @uses \phpDocumentor\Reflection\Types\Context + * @uses \phpDocumentor\Reflection\Types\String_ + * + * @covers ::create + */ + public function testCreateWithReference(): void + { + $descriptionFactory = m::mock(DescriptionFactory::class); + $resolver = new TypeResolver(); + $context = new Context(''); + + $description = new Description(''); + + $descriptionFactory->shouldReceive('create')->with('', $context)->andReturn($description); + + $fixture = Method::create( + 'string &myMethod()', + $resolver, + $descriptionFactory, + $context + ); + + $this->assertSame('string &myMethod()', (string) $fixture); + $this->assertSame('myMethod', $fixture->getMethodName()); + $this->assertEquals([], $fixture->getArguments()); + $this->assertInstanceOf(String_::class, $fixture->getReturnType()); + $this->assertSame($description, $fixture->getDescription()); + $this->assertTrue($fixture->returnsReference()); + } }