5
5
use PhpParser \Node ;
6
6
use PhpParser \Node \Expr \Variable ;
7
7
use PHPStan \Analyser \Scope ;
8
+ use PHPStan \Rules \IdentifierRuleError ;
8
9
use PHPStan \Rules \Rule ;
9
10
use PHPStan \Rules \RuleErrorBuilder ;
11
+ use PHPStan \Type \Constant \ConstantStringType ;
12
+ use PHPStan \Type \VerbosityLevel ;
13
+ use function array_map ;
14
+ use function array_merge ;
10
15
use function in_array ;
11
16
use function is_string ;
12
17
use function sprintf ;
@@ -31,11 +36,33 @@ public function getNodeType(): string
31
36
32
37
public function processNode (Node $ node , Scope $ scope ): array
33
38
{
34
- if (!is_string ($ node ->name )) {
35
- return [];
39
+ $ errors = [];
40
+ if (is_string ($ node ->name )) {
41
+ $ variableNames = [$ node ->name ];
42
+ } else {
43
+ $ fetchType = $ scope ->getType ($ node ->name );
44
+ $ variableNames = array_map (static fn (ConstantStringType $ type ): string => $ type ->getValue (), $ fetchType ->getConstantStrings ());
45
+ $ fetchStringType = $ fetchType ->toString ();
46
+ if (! $ fetchStringType ->isString ()->yes ()) {
47
+ $ errors [] = RuleErrorBuilder::message (sprintf ('Cannot access variable with a non-stringable type %s. ' , $ fetchType ->describe (VerbosityLevel::typeOnly ())))
48
+ ->identifier ('variable.fetchInvalidExpression ' )
49
+ ->build ();
50
+ }
51
+ }
52
+
53
+ foreach ($ variableNames as $ name ) {
54
+ $ errors = array_merge ($ errors , $ this ->processSingleVariable ($ scope , $ node , $ name ));
36
55
}
37
56
38
- if ($ this ->cliArgumentsVariablesRegistered && in_array ($ node ->name , [
57
+ return $ errors ;
58
+ }
59
+
60
+ /**
61
+ * @return list<IdentifierRuleError>
62
+ */
63
+ private function processSingleVariable (Scope $ scope , Variable $ node , string $ variableName ): array
64
+ {
65
+ if ($ this ->cliArgumentsVariablesRegistered && in_array ($ variableName , [
39
66
'argc ' ,
40
67
'argv ' ,
41
68
], true )) {
@@ -49,18 +76,18 @@ public function processNode(Node $node, Scope $scope): array
49
76
return [];
50
77
}
51
78
52
- if ($ scope ->hasVariableType ($ node -> name )->no ()) {
79
+ if ($ scope ->hasVariableType ($ variableName )->no ()) {
53
80
return [
54
- RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ node -> name ))
81
+ RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ variableName ))
55
82
->identifier ('variable.undefined ' )
56
83
->build (),
57
84
];
58
85
} elseif (
59
86
$ this ->checkMaybeUndefinedVariables
60
- && !$ scope ->hasVariableType ($ node -> name )->yes ()
87
+ && !$ scope ->hasVariableType ($ variableName )->yes ()
61
88
) {
62
89
return [
63
- RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ node -> name ))
90
+ RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ variableName ))
64
91
->identifier ('variable.undefined ' )
65
92
->build (),
66
93
];
0 commit comments