Skip to content

Commit ba8e860

Browse files
committed
Fix resolving class reflections for self static calls
1 parent 9b36647 commit ba8e860

File tree

3 files changed

+148
-10
lines changed

3 files changed

+148
-10
lines changed

src/Reflection/ParameterReflection.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use PhpParser\Node\Expr\StaticCall;
1212
use PhpParser\Node\Identifier;
1313
use PhpParser\Node\Name;
14-
use PHPStan\Reflection\ClassMemberAccessAnswerer;
14+
use PHPStan\Analyser\Scope;
1515
use PHPStan\Reflection\ExtendedParameterReflection;
1616
use PHPStan\Reflection\ReflectionProvider;
1717
use PHPStan\ShouldNotHappenException;
@@ -56,12 +56,15 @@ private function getStaticMethodArgs(StaticCall $node): array
5656
return [];
5757
}
5858

59-
$className = $this->nodeNameResolver->getName(node: $node->class);
60-
if (! $this->reflectionProvider->hasClass($className)) {
59+
$scope = $node->getAttribute(key: AttributeKey::SCOPE);
60+
if (! $scope instanceof Scope) {
6161
return [];
6262
}
6363

64-
$classReflection = $this->reflectionProvider->getClass($className);
64+
$classReflection = $this->resolveClassReflectionFromName(name: $node->class, scope: $scope);
65+
if ($classReflection === null) {
66+
return [];
67+
}
6568

6669
if ($node->name instanceof Identifier) {
6770
$methodName = $node->name->name;
@@ -75,8 +78,6 @@ private function getStaticMethodArgs(StaticCall $node): array
7578
return [];
7679
}
7780

78-
/** @var ClassMemberAccessAnswerer $scope */
79-
$scope = $node->getAttribute(key: AttributeKey::SCOPE);
8081
$reflection = $classReflection->getMethod(methodName: $methodName, scope: $scope);
8182

8283
try {
@@ -106,7 +107,6 @@ private function getMethodArgs(MethodCall $node): array
106107
return [];
107108
}
108109

109-
/** @var ClassMemberAccessAnswerer $scope */
110110
$scope = $node->getAttribute(key: AttributeKey::SCOPE);
111111
$reflection = $callerType->getMethod($methodName, $scope);
112112

@@ -201,4 +201,29 @@ private function resolveCalledName(Node $node): ?string
201201

202202
return null;
203203
}
204+
205+
private function resolveClassReflectionFromName(Name $name, Scope $scope): ?\PHPStan\Reflection\ClassReflection
206+
{
207+
$className = $this->nodeNameResolver->getName(node: $name);
208+
209+
if ($className === null) {
210+
return null;
211+
}
212+
213+
$lowerClassName = strtolower($className);
214+
215+
if (in_array($lowerClassName, ['self', 'static'], true)) {
216+
return $scope->getClassReflection();
217+
}
218+
219+
if ($lowerClassName === 'parent') {
220+
return $scope->getClassReflection()?->getParentClass();
221+
}
222+
223+
if (! $this->reflectionProvider->hasClass($className)) {
224+
return null;
225+
}
226+
227+
return $this->reflectionProvider->getClass($className);
228+
}
204229
}

src/Reflection/Reflection.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@
1010
use PhpParser\Node\Expr\New_;
1111
use PhpParser\Node\Expr\StaticCall;
1212
use PhpParser\Node\Name;
13+
use PHPStan\Analyser\Scope;
1314
use PHPStan\Reflection\ClassReflection;
1415
use PHPStan\Reflection\ExtendedParameterReflection;
1516
use PHPStan\Reflection\ReflectionProvider;
1617
use Rector\NodeNameResolver\NodeNameResolver;
18+
use Rector\NodeTypeResolver\Node\AttributeKey;
1719
use Rector\NodeTypeResolver\NodeTypeResolver;
1820
use ReflectionException;
1921
use ReflectionFunction;
@@ -132,19 +134,35 @@ public function getClassReflection(FuncCall|StaticCall|MethodCall|New_ $node): ?
132134
}
133135

134136
if ($node instanceof StaticCall && $node->class instanceof Name) {
135-
return $this->fetchClass($node->class);
137+
return $this->fetchClass($node->class, $node);
136138
}
137139

138140
if ($node instanceof New_ && $node->class instanceof Name) {
139-
return $this->fetchClass($node->class);
141+
return $this->fetchClass($node->class, $node);
140142
}
141143

142144
return null;
143145
}
144146

145-
private function fetchClass(Name $name): ?ClassReflection
147+
private function fetchClass(Name $name, Node $contextNode): ?ClassReflection
146148
{
147149
$className = $this->nodeNameResolver->getName(node: $name);
150+
if ($className === null) {
151+
return null;
152+
}
153+
154+
$scope = $contextNode->getAttribute(key: AttributeKey::SCOPE);
155+
if ($scope instanceof Scope) {
156+
$lowerClassName = strtolower($className);
157+
158+
if (in_array($lowerClassName, ['self', 'static'], true)) {
159+
return $scope->getClassReflection();
160+
}
161+
162+
if ($lowerClassName === 'parent') {
163+
return $scope->getClassReflection()?->getParentClass();
164+
}
165+
}
148166

149167
return $this->reflectionProvider->hasClass($className)
150168
? $this->reflectionProvider->getClass($className)
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
final class Article
6+
{
7+
public const string SLUID_TAG = 'tag';
8+
9+
private array $newTaxonomyItems = [];
10+
private array $currentAccessibleTaxonomyItems = [];
11+
12+
public function setTaxonomyItems(
13+
array $newTaxonomyItems,
14+
array $currentAccessibleTaxonomyItems,
15+
): void {
16+
$this->newTaxonomyItems = $newTaxonomyItems;
17+
$this->currentAccessibleTaxonomyItems = $currentAccessibleTaxonomyItems;
18+
}
19+
}
20+
21+
final class TestCase
22+
{
23+
public static function taxonomyItems(): void
24+
{
25+
$taxonomyTypeRepository = 'test';
26+
27+
$article = new Article();
28+
$article->setTaxonomyItems(
29+
[
30+
self::createTaxonomyItemByType(
31+
$taxonomyTypeRepository,
32+
Article::SLUID_TAG,
33+
'new-tag',
34+
),
35+
],
36+
[],
37+
);
38+
}
39+
40+
public static function createTaxonomyItemByType(
41+
string $taxonomyTypeRepository,
42+
string $taxonomyTypeSlUid,
43+
string $dutchLabel,
44+
): string {
45+
return $taxonomyTypeRepository . $taxonomyTypeSlUid . $dutchLabel;
46+
}
47+
}
48+
-----
49+
<?php
50+
51+
declare(strict_types=1);
52+
53+
final class Article
54+
{
55+
public const string SLUID_TAG = 'tag';
56+
57+
private array $newTaxonomyItems = [];
58+
private array $currentAccessibleTaxonomyItems = [];
59+
60+
public function setTaxonomyItems(
61+
array $newTaxonomyItems,
62+
array $currentAccessibleTaxonomyItems,
63+
): void {
64+
$this->newTaxonomyItems = $newTaxonomyItems;
65+
$this->currentAccessibleTaxonomyItems = $currentAccessibleTaxonomyItems;
66+
}
67+
}
68+
69+
final class TestCase
70+
{
71+
public static function taxonomyItems(): void
72+
{
73+
$taxonomyTypeRepository = 'test';
74+
75+
$article = new Article();
76+
$article->setTaxonomyItems(
77+
newTaxonomyItems: [
78+
self::createTaxonomyItemByType(
79+
taxonomyTypeRepository: $taxonomyTypeRepository,
80+
taxonomyTypeSlUid: Article::SLUID_TAG,
81+
dutchLabel: 'new-tag',
82+
),
83+
],
84+
currentAccessibleTaxonomyItems: [],
85+
);
86+
}
87+
88+
public static function createTaxonomyItemByType(
89+
string $taxonomyTypeRepository,
90+
string $taxonomyTypeSlUid,
91+
string $dutchLabel,
92+
): string {
93+
return $taxonomyTypeRepository . $taxonomyTypeSlUid . $dutchLabel;
94+
}
95+
}

0 commit comments

Comments
 (0)