-
Notifications
You must be signed in to change notification settings - Fork 432
/
PropertyReflectionFinder.php
128 lines (108 loc) · 3.8 KB
/
PropertyReflectionFinder.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
<?php declare(strict_types = 1);
namespace PHPStan\Rules\Properties;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\VarLikeIdentifier;
use PHPStan\Analyser\Scope;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\Type;
use function array_map;
class PropertyReflectionFinder
{
/**
* @param Node\Expr\PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
* @return FoundPropertyReflection[]
*/
public function findPropertyReflectionsFromNode($propertyFetch, Scope $scope): array
{
if ($propertyFetch instanceof Node\Expr\PropertyFetch) {
if ($propertyFetch->name instanceof Node\Identifier) {
$names = [$propertyFetch->name->name];
} else {
$names = array_map(static fn (ConstantStringType $name): string => $name->getValue(), $scope->getType($propertyFetch->name)->getConstantStrings());
}
$reflections = [];
$propertyHolderType = $scope->getType($propertyFetch->var);
foreach ($names as $name) {
$reflection = $this->findPropertyReflection(
$propertyHolderType,
$name,
$propertyFetch->name instanceof Expr ? $scope->filterByTruthyValue(new Expr\BinaryOp\Identical(
$propertyFetch->name,
new String_($name),
)) : $scope,
);
if ($reflection === null) {
continue;
}
$reflections[] = $reflection;
}
return $reflections;
}
if ($propertyFetch->class instanceof Node\Name) {
$propertyHolderType = $scope->getType(new Expr\ClassConstFetch($propertyFetch->class, 'class'))->getClassStringObjectType();
} else {
$propertyHolderType = $scope->getType($propertyFetch->class);
}
if ($propertyFetch->name instanceof VarLikeIdentifier) {
$names = [$propertyFetch->name->name];
} else {
$names = array_map(static fn (ConstantStringType $name): string => $name->getValue(), $scope->getType($propertyFetch->name)->getConstantStrings());
}
$reflections = [];
foreach ($names as $name) {
$reflection = $this->findPropertyReflection(
$propertyHolderType,
$name,
$propertyFetch->name instanceof Expr ? $scope->filterByTruthyValue(new Expr\BinaryOp\Identical(
$propertyFetch->name,
new String_($name),
)) : $scope,
);
if ($reflection === null) {
continue;
}
$reflections[] = $reflection;
}
return $reflections;
}
/**
* @param Node\Expr\PropertyFetch|Node\Expr\StaticPropertyFetch $propertyFetch
*/
public function findPropertyReflectionFromNode($propertyFetch, Scope $scope): ?FoundPropertyReflection
{
if ($propertyFetch instanceof Node\Expr\PropertyFetch) {
if (!$propertyFetch->name instanceof Node\Identifier) {
return null;
}
$propertyHolderType = $scope->getType($propertyFetch->var);
return $this->findPropertyReflection($propertyHolderType, $propertyFetch->name->name, $scope);
}
if (!$propertyFetch->name instanceof Node\Identifier) {
return null;
}
if ($propertyFetch->class instanceof Node\Name) {
$propertyHolderType = $propertyFetch->class->toLowerString() === 'static'
? $scope->getType(new Expr\ClassConstFetch($propertyFetch->class, 'class'))->getClassStringObjectType()
: $scope->resolveTypeByName($propertyFetch->class);
} else {
$propertyHolderType = $scope->getType($propertyFetch->class);
}
return $this->findPropertyReflection($propertyHolderType, $propertyFetch->name->name, $scope);
}
private function findPropertyReflection(Type $propertyHolderType, string $propertyName, Scope $scope): ?FoundPropertyReflection
{
if (!$propertyHolderType->hasProperty($propertyName)->yes()) {
return null;
}
$originalProperty = $propertyHolderType->getProperty($propertyName, $scope);
return new FoundPropertyReflection(
$originalProperty,
$scope,
$propertyName,
$originalProperty->getReadableType(),
$originalProperty->getWritableType(),
);
}
}