Skip to content

Commit d43bd25

Browse files
committed
Treat leading $variable as a valid @see reference
Previously `@see $varname` failed FQSEN resolution and was surfaced as an InvalidTag (and in the PHP 8.0/8.1 versions mentioned in #335 it even aborted parsing). The `See` factory now detects a bare variable identifier and wraps it in a new `Reference\\Variable` alongside the existing `Reference\\Fqsen` and `Reference\\Url`, so global-variable references survive parsing and round-trip. Fixes #335
1 parent 7bae675 commit d43bd25

3 files changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\DocBlock\Tags\Reference;
15+
16+
use Webmozart\Assert\Assert;
17+
18+
/**
19+
* Variable reference used by {@see \phpDocumentor\Reflection\DocBlock\Tags\See} to refer to a variable that
20+
* is not addressable through an FQSEN, typically a global variable such as {@example @see $varname}.
21+
*/
22+
final class Variable implements Reference
23+
{
24+
private string $name;
25+
26+
public function __construct(string $name)
27+
{
28+
Assert::stringNotEmpty($name);
29+
Assert::startsWith($name, '$');
30+
31+
$this->name = $name;
32+
}
33+
34+
public function __toString(): string
35+
{
36+
return $this->name;
37+
}
38+
}

src/DocBlock/Tags/See.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
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\DocBlock\Tags\Reference\Variable;
2122
use phpDocumentor\Reflection\Fqsen;
2223
use phpDocumentor\Reflection\FqsenResolver;
2324
use phpDocumentor\Reflection\Types\Context as TypeContext;
@@ -62,6 +63,11 @@ public static function create(
6263
return new static(new Url($parts[0]), $description);
6364
}
6465

66+
// Variables are not addressable through an FQSEN but are a valid target for {@}see, e.g. a global `$varname`.
67+
if (preg_match('/^\$[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $parts[0])) {
68+
return new static(new Variable($parts[0]), $description);
69+
}
70+
6571
return new static(new FqsenRef(self::resolveFqsen($parts[0], $typeResolver, $context)), $description);
6672
}
6773

tests/unit/DocBlock/Tags/SeeTest.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as FqsenRef;
2121
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Fqsen as TagsFqsen;
2222
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Url as UrlRef;
23+
use phpDocumentor\Reflection\DocBlock\Tags\Reference\Variable as VariableRef;
2324
use phpDocumentor\Reflection\Fqsen;
2425
use phpDocumentor\Reflection\FqsenResolver;
2526
use phpDocumentor\Reflection\Types\Context;
@@ -253,6 +254,37 @@ public function testFactoryMethodWithUrl(): void
253254
$this->assertSame($description, $fixture->getDescription());
254255
}
255256

257+
/**
258+
* @uses \phpDocumentor\Reflection\DocBlock\Tags\See::<public>
259+
* @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory
260+
* @uses \phpDocumentor\Reflection\FqsenResolver
261+
* @uses \phpDocumentor\Reflection\DocBlock\Description
262+
* @uses \phpDocumentor\Reflection\DocBlock\Tags\Reference\Variable
263+
* @uses \phpDocumentor\Reflection\Types\Context
264+
*
265+
* @covers ::create
266+
*/
267+
public function testFactoryMethodWithVariable(): void
268+
{
269+
$descriptionFactory = m::mock(DescriptionFactory::class);
270+
$resolver = m::mock(FqsenResolver::class);
271+
$context = new Context('');
272+
273+
$description = new Description('My Description');
274+
275+
$descriptionFactory
276+
->shouldReceive('create')->with('My Description', $context)->andReturn($description);
277+
278+
$resolver->shouldNotReceive('resolve');
279+
280+
$fixture = See::create('$varname My Description', $resolver, $descriptionFactory, $context);
281+
282+
$this->assertSame('$varname My Description', (string) $fixture);
283+
$this->assertInstanceOf(VariableRef::class, $fixture->getReference());
284+
$this->assertSame('$varname', (string) $fixture->getReference());
285+
$this->assertSame($description, $fixture->getDescription());
286+
}
287+
256288
/**
257289
* @uses \phpDocumentor\Reflection\DocBlock\Tags\See::<public>
258290
* @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory

0 commit comments

Comments
 (0)