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 820d595..582be6c 100644 --- a/src/DocBlock/Tags/Covers.php +++ b/src/DocBlock/Tags/Covers.php @@ -20,6 +20,8 @@ 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; /** * Reflection class for a @covers tag in a Docblock. @@ -54,11 +56,24 @@ final class Covers extends BaseTag implements Factory\StaticMethod $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 e71401d..2c77f86 100644 --- a/src/DocBlock/Tags/See.php +++ b/src/DocBlock/Tags/See.php @@ -18,10 +18,13 @@ use phpDocumentor\Reflection\DocBlock\DescriptionFactory; 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 @@ final class See extends BaseTag implements Factory\StaticMethod ?DescriptionFactory $descriptionFactory = null, ?TypeContext $context = null ) : self { - Assert::notNull($typeResolver); Assert::notNull($descriptionFactory); $parts = Utils::pregSplit('/\s+/Su', $body, 2); @@ -61,7 +63,20 @@ final class See extends BaseTag implements Factory\StaticMethod 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 57fb290..2f6e8f1 100644 --- a/src/DocBlock/Tags/Uses.php +++ b/src/DocBlock/Tags/Uses.php @@ -20,6 +20,8 @@ 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; /** * Reflection class for a {@}uses tag in a Docblock. @@ -53,11 +55,24 @@ final class Uses extends BaseTag implements Factory\StaticMethod $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 0000000..f98a888 --- /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 f9ed04c..c8e5642 100644 --- a/tests/unit/DocBlock/Tags/SeeTest.php +++ b/tests/unit/DocBlock/Tags/SeeTest.php @@ -167,6 +167,38 @@ class SeeTest extends TestCase $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