Skip to content

Commit 73650dd

Browse files
committed
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.
1 parent 08c0b36 commit 73650dd

File tree

5 files changed

+119
-4
lines changed

5 files changed

+119
-4
lines changed

src/DocBlock/Tags/Covers.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use phpDocumentor\Reflection\Types\Context as TypeContext;
2121
use phpDocumentor\Reflection\Utils;
2222
use Webmozart\Assert\Assert;
23+
use function array_key_exists;
24+
use function explode;
2325

2426
/**
2527
* Reflection class for a @covers tag in a Docblock.
@@ -54,11 +56,24 @@ public static function create(
5456
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
5557

5658
return new static(
57-
$resolver->resolve($parts[0], $context),
59+
self::resolveFqsen($parts[0], $resolver, $context),
5860
$descriptionFactory->create($parts[1] ?? '', $context)
5961
);
6062
}
6163

64+
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
65+
{
66+
Assert::notNull($fqsenResolver);
67+
$fqsenParts = explode('::', $parts);
68+
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
69+
70+
if (!array_key_exists(1, $fqsenParts)) {
71+
return $resolved;
72+
}
73+
74+
return new Fqsen($resolved . '::' . $fqsenParts[1]);
75+
}
76+
6277
/**
6378
* Returns the structural element this tag refers to.
6479
*/

src/DocBlock/Tags/See.php

+17-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef;
1919
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Reference;
2020
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url;
21+
use phpDocumentor\Reflection\Fqsen;
2122
use phpDocumentor\Reflection\FqsenResolver;
2223
use phpDocumentor\Reflection\Types\Context as TypeContext;
2324
use phpDocumentor\Reflection\Utils;
2425
use Webmozart\Assert\Assert;
26+
use function array_key_exists;
27+
use function explode;
2528
use function preg_match;
2629

2730
/**
@@ -50,7 +53,6 @@ public static function create(
5053
?DescriptionFactory $descriptionFactory = null,
5154
?TypeContext $context = null
5255
) : self {
53-
Assert::notNull($typeResolver);
5456
Assert::notNull($descriptionFactory);
5557

5658
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
@@ -61,7 +63,20 @@ public static function create(
6163
return new static(new Url($parts[0]), $description);
6264
}
6365

64-
return new static(new FqsenRef($typeResolver->resolve($parts[0], $context)), $description);
66+
return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description);
67+
}
68+
69+
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
70+
{
71+
Assert::notNull($fqsenResolver);
72+
$fqsenParts = explode('::', $parts);
73+
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
74+
75+
if (!array_key_exists(1, $fqsenParts)) {
76+
return $resolved;
77+
}
78+
79+
return new Fqsen($resolved . '::' . $fqsenParts[1]);
6580
}
6681

6782
/**

src/DocBlock/Tags/Uses.php

+16-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
use phpDocumentor\Reflection\Types\Context as TypeContext;
2121
use phpDocumentor\Reflection\Utils;
2222
use Webmozart\Assert\Assert;
23+
use function array_key_exists;
24+
use function explode;
2325

2426
/**
2527
* Reflection class for a {@}uses tag in a Docblock.
@@ -53,11 +55,24 @@ public static function create(
5355
$parts = Utils::pregSplit('/\s+/Su', $body, 2);
5456

5557
return new static(
56-
$resolver->resolve($parts[0], $context),
58+
self::resolveFqsen($parts[0], $resolver, $context),
5759
$descriptionFactory->create($parts[1] ?? '', $context)
5860
);
5961
}
6062

63+
private static function resolveFqsen(string $parts, ?FqsenResolver $fqsenResolver, ?TypeContext $context) : Fqsen
64+
{
65+
Assert::notNull($fqsenResolver);
66+
$fqsenParts = explode('::', $parts);
67+
$resolved = $fqsenResolver->resolve($fqsenParts[0], $context);
68+
69+
if (!array_key_exists(1, $fqsenParts)) {
70+
return $resolved;
71+
}
72+
73+
return new Fqsen($resolved . '::' . $fqsenParts[1]);
74+
}
75+
6176
/**
6277
* Returns the structural element this tag refers to.
6378
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace phpDocumentor\Reflection;
6+
7+
use phpDocumentor\Reflection\DocBlock\Tags\See;
8+
use phpDocumentor\Reflection\Types\Context;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class DocblockSeeTagResolvingTest extends TestCase
12+
{
13+
public function testResolvesSeeFQSENOfInlineTags()
14+
{
15+
$context = new Context('\Project\Sub\Level', ['Issue2425B' => '\Project\Other\Level\Issue2425B', 'Aliased' => 'Project\Other\Level\Issue2425C']);
16+
$docblockString = <<<DOCBLOCK
17+
/**
18+
* Class summary.
19+
*
20+
* A description containing an inline {@see Issue2425B::bar()} tag
21+
* to a class inside of the project referenced via a use statement.
22+
*
23+
* And here is another inline {@see Aliased::bar()} tag to a class
24+
* aliased via a use statement.
25+
*/
26+
DOCBLOCK;
27+
28+
29+
30+
$factory = DocBlockFactory::createInstance();
31+
$docblock = $factory->create($docblockString, $context);
32+
33+
/** @var See $see1 */
34+
$see1 = $docblock->getDescription()->getTags()[0];
35+
36+
$this->assertSame('\Project\Other\Level\Issue2425B::bar()', (string)$see1->getReference());
37+
}
38+
}

tests/unit/DocBlock/Tags/SeeTest.php

+32
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,38 @@ public function testFactoryMethod() : void
167167
$this->assertSame($description, $fixture->getDescription());
168168
}
169169

170+
/**
171+
* @uses \phpDocumentor\Reflection\DocBlock\Tags\See::<public>
172+
* @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory
173+
* @uses \phpDocumentor\Reflection\FqsenResolver
174+
* @uses \phpDocumentor\Reflection\DocBlock\Description
175+
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen
176+
* @uses \phpDocumentor\Reflection\Fqsen
177+
* @uses \phpDocumentor\Reflection\Types\Context
178+
*
179+
* @covers ::create
180+
*/
181+
public function testFactoryMethodWithNonClassFQSEN() : void
182+
{
183+
$descriptionFactory = m::mock(DescriptionFactory::class);
184+
$resolver = m::mock(FqsenResolver::class);
185+
$context = new Context('');
186+
187+
$fqsen = new Fqsen('\DateTime');
188+
$description = new Description('My Description');
189+
190+
$descriptionFactory
191+
->shouldReceive('create')->with('My Description', $context)->andReturn($description);
192+
$resolver->shouldReceive('resolve')->with('DateTime', $context)->andReturn($fqsen);
193+
194+
$fixture = See::create('DateTime::createFromFormat() My Description', $resolver, $descriptionFactory, $context);
195+
196+
$this->assertSame('\DateTime::createFromFormat() My Description', (string) $fixture);
197+
$this->assertInstanceOf(FqsenRef::class, $fixture->getReference());
198+
$this->assertSame('\DateTime::createFromFormat()', (string) $fixture->getReference());
199+
$this->assertSame($description, $fixture->getDescription());
200+
}
201+
170202
/**
171203
* @uses \phpDocumentor\Reflection\DocBlock\Tags\See::<public>
172204
* @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory

0 commit comments

Comments
 (0)