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 \Rules \RuleLevelHelper ;
12+ use PHPStan \Type \Constant \ConstantStringType ;
13+ use PHPStan \Type \VerbosityLevel ;
14+ use function array_map ;
15+ use function array_merge ;
1016use function in_array ;
1117use function is_string ;
1218use function sprintf ;
@@ -20,6 +26,7 @@ final class DefinedVariableRule implements Rule
2026 public function __construct (
2127 private bool $ cliArgumentsVariablesRegistered ,
2228 private bool $ checkMaybeUndefinedVariables ,
29+ // private RuleLevelHelper $ruleLevelHelper,
2330 )
2431 {
2532 }
@@ -31,11 +38,33 @@ public function getNodeType(): string
3138
3239 public function processNode (Node $ node , Scope $ scope ): array
3340 {
34- if (!is_string ($ node ->name )) {
35- return [];
41+ $ errors = [];
42+ if (is_string ($ node ->name )) {
43+ $ variableNames = [$ node ->name ];
44+ } else {
45+ $ fetchType = $ scope ->getType ($ node ->name );
46+ $ variableNames = array_map (static fn (ConstantStringType $ type ): string => $ type ->getValue (), $ fetchType ->getConstantStrings ());
47+ $ fetchStringType = $ fetchType ->toString ();
48+ if (! $ fetchStringType ->isString ()->yes ()) {
49+ $ errors [] = RuleErrorBuilder::message (sprintf ('Cannot access variable with a non-stringable type %s. ' , $ fetchType ->describe (VerbosityLevel::typeOnly ())))
50+ ->identifier ('variable.fetchInvalidExpression ' )
51+ ->build ();
52+ }
53+ }
54+
55+ foreach ($ variableNames as $ name ) {
56+ $ errors = array_merge ($ errors , $ this ->processSingleVariable ($ scope , $ node , $ name ));
3657 }
3758
38- if ($ this ->cliArgumentsVariablesRegistered && in_array ($ node ->name , [
59+ return $ errors ;
60+ }
61+
62+ /**
63+ * @return list<IdentifierRuleError>
64+ */
65+ private function processSingleVariable (Scope $ scope , Variable $ node , string $ variableName ): array
66+ {
67+ if ($ this ->cliArgumentsVariablesRegistered && in_array ($ variableName , [
3968 'argc ' ,
4069 'argv ' ,
4170 ], true )) {
@@ -49,18 +78,18 @@ public function processNode(Node $node, Scope $scope): array
4978 return [];
5079 }
5180
52- if ($ scope ->hasVariableType ($ node -> name )->no ()) {
81+ if ($ scope ->hasVariableType ($ variableName )->no ()) {
5382 return [
54- RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ node -> name ))
83+ RuleErrorBuilder::message (sprintf ('Undefined variable: $%s ' , $ variableName ))
5584 ->identifier ('variable.undefined ' )
5685 ->build (),
5786 ];
5887 } elseif (
5988 $ this ->checkMaybeUndefinedVariables
60- && !$ scope ->hasVariableType ($ node -> name )->yes ()
89+ && !$ scope ->hasVariableType ($ variableName )->yes ()
6190 ) {
6291 return [
63- RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ node -> name ))
92+ RuleErrorBuilder::message (sprintf ('Variable $%s might not be defined. ' , $ variableName ))
6493 ->identifier ('variable.undefined ' )
6594 ->build (),
6695 ];
0 commit comments