8000 bug #45890 [PropertyInfo] PhpStanExtractor namespace missmatch issue … · symfony/symfony@8352e29 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8352e29

Browse files
bug #45890 [PropertyInfo] PhpStanExtractor namespace missmatch issue (Korbeil)
This PR was merged into the 5.4 branch. Discussion ---------- [PropertyInfo] PhpStanExtractor namespace missmatch issue | Q | A | ------------- | --- | Branch? | 5.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #44431 | License | MIT | Doc PR | N/A Thanks to `@isypov`-andrey report, we found out an issue with namespace matching and the PhpStanExtractor. In this PR I'm fixing this issue. - [x] Test to validate the issue - [x] Fix Commits ------- d46b676 [PropertyInfo] PhpStanExtractor namespace missmatch issue
2 parents 3a76820 + d46b676 commit 8352e29

File tree

5 files changed

+69
-26
lines changed

5 files changed

+69
-26
lines changed

src/Symfony/Component/PropertyInfo/Extractor/PhpStanExtractor.php

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ final class PhpStanExtractor implements PropertyTypeExtractorInterface, Construc
4545
/** @var NameScopeFactory */
4646
private $nameScopeFactory;
4747

48-
/** @var array<string, array{PhpDocNode|null, int|null, string|null}> */
48+
/** @var array<string, array{PhpDocNode|null, int|null, string|null, string|null}> */
4949
private $docBlocks = [];
5050
private $phpStanTypeHelper;
5151
private $mutatorPrefixes;
@@ -72,8 +72,8 @@ public function __construct(array $mutatorPrefixes = null, array $accessorPrefix
7272
public function getTypes(string $class, string $property, array $context = []): ?array
7373
{
7474
/** @var PhpDocNode|null $docNode */
75-
[$docNode, $source, $prefix] = $this->getDocBlock($class, $property);
76-
$nameScope = $this->nameScopeFactory->create($class);
75+
[$docNode, $source, $prefix, $declaringClass] = $this->getDocBlock($class, $property);
76+
$nameScope = $this->nameScopeFactory->create($class, $declaringClass);
7777
if (null === $docNode) {
7878
return null;
7979
}
@@ -184,7 +184,7 @@ private function filterDocBlockParams(PhpDocNode $docNode, string $allowedParam)
184184
}
185185

186186
/**
187-
* @return array{PhpDocNode|null, int|null, string|null}
187+
* @return array{PhpDocNode|null, int|null, string|null, string|null}
188188
*/
189189
private function getDocBlock(string $class, string $property): array
190190
{
@@ -196,20 +196,23 @@ private function getDocBlock(string $class, string $property): array
196196

197197
$ucFirstProperty = ucfirst($property);
198198

199-
if ($docBlock = $this->getDocBlockFromProperty($class, $property)) {
200-
$data = [$docBlock, self::PROPERTY, null];
201-
} elseif ([$docBlock] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)) {
202-
$data = [$docBlock, self::ACCESSOR, null];
203-
} elseif ([$docBlock, $prefix] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)) {
204-
$data = [$docBlock, self::MUTATOR, $prefix];
199+
if ([$docBlock, $declaringClass] = $this->getDocBlockFromProperty($class, $property)) {
200+
$data = [$docBlock, self::PROPERTY, null, $declaringClass];
201+
} elseif ([$docBlock, $_, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::ACCESSOR)) {
202+
$data = [$docBlock, self::ACCESSOR, null, $declaringClass];
203+
} elseif ([$docBlock, $prefix, $declaringClass] = $this->getDocBlockFromMethod($class, $ucFirstProperty, self::MUTATOR)) {
204+
$data = [$docBlock, self::MUTATOR, $prefix, $declaringClass];
205205
} else {
206-
$data = [null, null, null];
206+
$data = [null, null, null, null];
207207
}
208208

209209
return $this->docBlocks[$propertyHash] = $data;
210210
}
211211

212-
private function getDocBlockFromProperty(string $class, string $property): ?PhpDocNode
212+
/**
213+
* @return array{PhpDocNode, string}|null
214+
*/
215+
private function getDocBlockFromProperty(string $class, string $property): ?array
213216
{
214217
// Use a ReflectionProperty instead of $class to get the parent class if applicable
215218
try {
@@ -226,11 +229,11 @@ private function getDocBlockFromProperty(string $class, string $property): ?PhpD
226229
$phpDocNode = $this->phpDocParser->parse($tokens);
227230
$tokens->consumeTokenType(Lexer::TOKEN_END);
228231

229-
return $phpDocNode;
232+
return [$phpDocNode, $reflectionProperty->class];
230233
}
231234

232235
/**
233-
* @return array{PhpDocNode, string}|null
236+
* @return array{PhpDocNode, string, string}|null
234237
*/
235238
private function getDocBlockFromMethod(string $class, string $ucFirstProperty, int $type): ?array
236239
{
@@ -269,6 +272,6 @@ private function getDocBlockFromMethod(string $class, string $ucFirstProperty, i
269272
$phpDocNode = $this->phpDocParser->parse($tokens);
270273
$tokens->consumeTokenType(Lexer::TOKEN_END);
271274

272-
return [$phpDocNode, $prefix];
275+
return [$phpDocNode, $prefix, $reflectionMethod->class];
273276
}
274277
}

src/Symfony/Component/PropertyInfo/PhpStan/NameScope.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@
2222
*/
2323
final class NameScope
2424
{
25-
private $className;
25+
private $calledClassName;
2626
private $namespace;
2727
/** @var array<string, string> alias(string) => fullName(string) */
2828
private $uses;
2929

30-
public function __construct(string $className, string $namespace, array $uses = [])
30+
public function __construct(string $calledClassName, string $namespace, array $uses = [])
3131
{
32-
$this->className = $className;
32+
$this->calledClassName = $calledClassName;
3333
$this->namespace = $namespace;
3434
$this->uses = $uses;
3535
}
@@ -60,6 +60,6 @@ public function resolveStringName(string $name): string
6060

6161
public function resolveRootClass(): string
6262
{
63-
return $this->resolveStringName($this->className);
63+
return $this->resolveStringName($this->calledClassName);
6464
}
6565
}

src/Symfony/Component/PropertyInfo/PhpStan/NameScopeFactory.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@
2020
*/
2121
final class NameScopeFactory
2222
{
23-
public function create(string $fullClassName): NameScope
23+
public function create(string $calledClassName, string $declaringClassName = null): NameScope
2424
{
25-
$reflection = new \ReflectionClass($fullClassName);
26-
$path = explode('\\', $fullClassName);
27-
$className = array_pop($path);
28-
[$namespace, $uses] = $this->extractFromFullClassName($reflection);
25+
$declaringClassName = $declaringClassName ?? $calledClassName;
2926

30-
$uses = array_merge($uses, $this->collectUses($reflection));
27+
$path = explode('\\', $calledClassName);
28+
$calledClassName = array_pop($path);
3129

32-
return new NameScope($className, $namespace, $uses);
30+
$declaringReflection = new \ReflectionClass($declaringClassName);
31+
[$declaringNamespace, $declaringUses] = $this->extractFromFullClassName($declaringReflection);
32+
$declaringUses = array_merge($declaringUses, $this->collectUses($declaringReflection));
33+
34+
return new NameScope($calledClassName, $declaringNamespace, $declaringUses);
3335
}
3436

3537
private function collectUses(\ReflectionClass $reflection): array

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\PropertyInfo\Tests\Extractor;
1313

1414
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\PropertyInfo\Extractor\PhpDocExtractor;
1516
use Symfony\Component\PropertyInfo\Extractor\PhpStanExtractor;
1617
use Symfony\Component\PropertyInfo\Tests\Fixtures\DefaultValue;
1718
use Symfony\Component\PropertyInfo\Tests\Fixtures\Dummy;
@@ -21,6 +22,8 @@
2122
use Symfony\Component\PropertyInfo\Tests\Fixtures\TraitUsage\DummyUsingTrait;
2223
use Symfony\Component\PropertyInfo\Type;
2324

25+
require_once __DIR__.'/../Fixtures/Extractor/DummyNamespace.php';
26+
2427
/**
2528
* @author Baptiste Leduc <baptiste.leduc@gmail.com>
2629
*/
@@ -31,9 +34,15 @@ class PhpStanExtractorTest extends TestCase
3134
*/
3235
private $extractor;
3336

37+
/**
38+
* @var PhpDocExtractor
39+
*/
40+
private $phpDocExtractor;
41+
3442
protected function setUp(): void
3543
{
3644
$this->extractor = new PhpStanExtractor();
45+
$this->phpDocExtractor = new PhpDocExtractor();
3746
}
3847

3948
/**
@@ -383,6 +392,15 @@ public function testDummyNamespace()
383392
$this->extractor->getTypes('Symfony\Component\PropertyInfo\Tests\Fixtures\DummyNamespace', 'dummy')
384393
);
385394
}
395+
396+
public function testDummyNamespaceWithProperty()
397+
{
398+
$phpStanTypes = $this->extractor->getTypes(\B\Dummy::class, 'property');
399+
$phpDocTypes = $this->phpDocExtractor->getTypes(\B\Dummy::class, 'property');
400+
401+
$this->assertEquals('A\Property', $phpStanTypes[0]->getClassName());
402+
$this->assertEquals($phpDocTypes[0]->getClassName(), $phpStanTypes[0]->getClassName());
403+
}
386404
}
387405

388406
class PhpStanOmittedParamTagTypeDocBlock
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace A {
4+
class Property {
5+
6+
}
7+
8+
class Dummy {
9+
/**
10+
* @var Property
11+
*/
12+
public $property;
13+
}
14+
}
15+
16+
namespace B {
17+
class Dummy extends \A\Dummy {
18+
19+
}
20+
}

0 commit comments

Comments
 (0)
0