55use PhpParser \Node ;
66use PhpParser \Node \Expr \Variable ;
77use PHPStan \Analyser \Scope ;
8+ use PHPStan \Rules \IdentifierRuleError ;
89use PHPStan \Rules \Rule ;
910use PHPStan \Rules \RuleErrorBuilder ;
11+ use PHPStan \Type \Constant \ConstantStringType ;
12+ use PHPStan \Type \VerbosityLevel ;
13+ use function array_map ;
14+ use function array_merge ;
1015use function in_array ;
1116use function is_string ;
1217use function sprintf ;
@@ -31,11 +36,33 @@ public function getNodeType(): string
3136
3237 public function processNode (Node $ node , Scope $ scope ): array
3338 {
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 ));
3655 }
3756
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 , [
3966 'argc ' ,
4067 'argv ' ,
4168 ], true )) {
@@ -49,18 +76,18 @@ public function processNode(Node $node, Scope $scope): array
4976 return [];
5077 }
5178
52- if ($ scope ->hasVariableType ($ node -> name )->no ()) {
79+ if ($ scope ->hasVariableType ($ variableName )->no ()) {
5380 return [
54- RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ node -> name ))
81+ RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ variableName ))
5582 ->identifier ('variable.undefined ' )
5683 ->build (),
5784 ];
5885 } elseif (
5986 $ this ->checkMaybeUndefinedVariables
60- && !$ scope ->hasVariableType ($ node -> name )->yes ()
87+ && !$ scope ->hasVariableType ($ variableName )->yes ()
6188 ) {
6289 return [
63- RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ node -> name ))
90+ RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ variableName ))
6491 ->identifier ('variable.undefined ' )
6592 ->build (),
6693 ];
0 commit comments