From 73650dde9181b643ca9d12cf1143c61ca4901f75 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Thu, 18 Jun 2020 23:48:32 +0200 Subject: [PATCH] Fix FQSEN resolving on see,covers,uses The See, Covers and Use tags can reference also methods, properties and constants. Which means that the FqsenResolver cannot handle those properly. This patch fixes that issue. --- src/DocBlock/Tags/Covers.php | 17 ++++++++- src/DocBlock/Tags/See.php | 19 +++++++++- src/DocBlock/Tags/Uses.php | 17 ++++++++- .../DocblockSeeTagResolvingTest.php | 38 +++++++++++++++++++ tests/unit/DocBlock/Tags/SeeTest.php | 32 ++++++++++++++++ 5 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 tests/integration/DocblockSeeTagResolvingTest.php diff --git a/src/DocBlock/Tags/Covers.php b/src/DocBlock/Tags/Covers.php index 820d5952..582be6ce 100644 --- a/src/DocBlock/Tags/Covers.php +++ b/src/DocBlock/Tags/Covers.php @@ -20,6 +20,8 @@ use phpDocumentor\Reflection\Types\Context as TypeContext; use phpDocumentor\Reflection\Utils; use Webmozart\Assert\Assert; +use function array_key_exists; +use function explode; /** * Reflection class for a @covers tag in a Docblock. @@ -54,11 +56,24 @@ public static function create( $parts = Utils::pregSplit('/\s+/Su', $body, 2); return new static( - $resolver->resolve($parts[0], $context), + self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context) ); } + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + /** * Returns the structural element this tag refers to. */ diff --git a/src/DocBlock/Tags/See.php b/src/DocBlock/Tags/See.php index e71401d0..2c77f867 100644 --- a/src/DocBlock/Tags/See.php +++ b/src/DocBlock/Tags/See.php @@ -18,10 +18,13 @@ use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef; use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference; use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url; +use phpDocumentor\Reflection\Fqsen; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; use phpDocumentor\Reflection\Utils; use Webmozart\Assert\Assert; +use function array_key_exists; +use function explode; use function preg_match; /** @@ -50,7 +53,6 @@ public static function create( ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null ) : self { - Assert::notNull($typeResolver); Assert::notNull($descriptionFactory); $parts = Utils::pregSplit('/\s+/Su', $body, 2); @@ -61,7 +63,20 @@ public static function create( return new static(new Url($parts[0]), $description); } - return new static(new FqsenRef($typeResolver->resolve($parts[0], $context)), $description); + return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description); + } + + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); } /** diff --git a/src/DocBlock/Tags/Uses.php b/src/DocBlock/Tags/Uses.php index 57fb2908..2f6e8f1c 100644 --- a/src/DocBlock/Tags/Uses.php +++ b/src/DocBlock/Tags/Uses.php @@ -20,6 +20,8 @@ use phpDocumentor\Reflection\Types\Context as TypeContext; use phpDocumentor\Reflection\Utils; use Webmozart\Assert\Assert; +use function array_key_exists; +use function explode; /** * Reflection class for a {@}uses tag in a Docblock. @@ -53,11 +55,24 @@ public static function create( $parts = Utils::pregSplit('/\s+/Su', $body, 2); return new static( - $resolver->resolve($parts[0], $context), + self::resolveFqsen($parts[0], $resolver, $context), $descriptionFactory->create($parts[1] ?? '', $context) ); } + private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen + { + Assert::notNull($fqsenResolver); + $fqsenParts = explode('::', $parts); + $resolved = $fqsenResolver->resolve($fqsenParts[0], $context); + + if (!array_key_exists(1, $fqsenParts)) { + return $resolved; + } + + return new Fqsen($resolved . '::' . $fqsenParts[1]); + } + /** * Returns the structural element this tag refers to. */ diff --git a/tests/integration/DocblockSeeTagResolvingTest.php b/tests/integration/DocblockSeeTagResolvingTest.php new file mode 100644 index 00000000..f98a8881 --- /dev/null +++ b/tests/integration/DocblockSeeTagResolvingTest.php @@ -0,0 +1,38 @@ + '\Project\Other\Level\Issue2425B', 'Aliased' => 'Project\Other\Level\Issue2425C']); + $docblockString = <<create($docblockString, $context); + + /** @var See $see1 */ + $see1 = $docblock->getDescription()->getTags()[0]; + + $this->assertSame('\Project\Other\Level\Issue2425B::bar()', (string)$see1->getReference()); + } +} diff --git a/tests/unit/DocBlock/Tags/SeeTest.php b/tests/unit/DocBlock/Tags/SeeTest.php index f9ed04c0..c8e56424 100644 --- a/tests/unit/DocBlock/Tags/SeeTest.php +++ b/tests/unit/DocBlock/Tags/SeeTest.php @@ -167,6 +167,38 @@ public function testFactoryMethod() : void $this->assertSame($description, $fixture->getDescription()); } + /** + * @uses \phpDocumentor\Reflection\DocBlock\Tags\See:: + * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory + * @uses \phpDocumentor\Reflection\FqsenResolver + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen + * @uses \phpDocumentor\Reflection\Fqsen + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::create + */ + public function testFactoryMethodWithNonClassFQSEN() : void + { + $descriptionFactory = m::mock(DescriptionFactory::class); + $resolver = m::mock(FqsenResolver::class); + $context = new Context(''); + + $fqsen = new Fqsen('\DateTime'); + $description = new Description('My Description'); + + $descriptionFactory + ->shouldReceive('create')->with('My Description', $context)->andReturn($description); + $resolver->shouldReceive('resolve')->with('DateTime', $context)->andReturn($fqsen); + + $fixture = See::create('DateTime::createFromFormat() My Description', $resolver, $descriptionFactory, $context); + + $this->assertSame('\DateTime::createFromFormat() My Description', (string) $fixture); + $this->assertInstanceOf(FqsenRef::class, $fixture->getReference()); + $this->assertSame('\DateTime::createFromFormat()', (string) $fixture->getReference()); + $this->assertSame($description, $fixture->getDescription()); + } + /** * @uses \phpDocumentor\Reflection\DocBlock\Tags\See:: * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory