5
5
use LogicException ;
6
6
use PhpParser \Node ;
7
7
use PhpParser \Node \Expr ;
8
+ use PhpParser \Node \Expr \ClassConstFetch ;
8
9
use PhpParser \Node \Expr \PropertyFetch ;
9
10
use PhpParser \Node \Expr \StaticPropertyFetch ;
10
11
use PhpParser \Node \Identifier ;
11
12
use PhpParser \PrettyPrinter \Standard ;
12
13
use PHPStan \Analyser \Scope ;
13
14
use PHPStan \Rules \Rule ;
15
+ use PHPStan \Type \Type ;
14
16
use PHPStan \Type \TypeUtils ;
15
17
use function get_class ;
16
18
use function sprintf ;
@@ -45,15 +47,15 @@ public function processNode(Node $node, Scope $scope): array
45
47
return []; // already checked by native PHPStan
46
48
}
47
49
48
- if ($ node instanceof PropertyFetch || $ node instanceof StaticPropertyFetch) {
50
+ if ($ node instanceof PropertyFetch || $ node instanceof StaticPropertyFetch || $ node instanceof ClassConstFetch ) {
49
51
return $ this ->processFetch ($ node , $ scope );
50
52
}
51
53
52
54
return [];
53
55
}
54
56
55
57
/**
56
- * @param PropertyFetch|StaticPropertyFetch $node
58
+ * @param PropertyFetch|StaticPropertyFetch|ClassConstFetch $node
57
59
* @return list<string>
58
60
*/
59
61
private function processFetch (Node $ node , Scope $ scope ): array
@@ -68,17 +70,24 @@ private function processFetch(Node $node, Scope $scope): array
68
70
69
71
$ callerType = TypeUtils::toBenevolentUnion ($ scope ->getType ($ caller ));
70
72
71
- if ($ callerType ->getObjectTypeOrClassStringObjectType ()->getObjectClassNames () === []) {
73
+ if (
74
+ $ callerType ->getObjectTypeOrClassStringObjectType ()->getObjectClassNames () === []
75
+ && !$ this ->isObjectClassFetch ($ callerType , $ node )
76
+ ) {
72
77
$ name = $ node ->name ;
73
- $ property = $ name instanceof Identifier
78
+ $ propertyOrConstant = $ name instanceof Identifier
74
79
? $ this ->printer ->prettyPrint ([$ name ])
75
80
: $ this ->printer ->prettyPrintExpr ($ name );
81
+ $ element = $ node instanceof ClassConstFetch
82
+ ? 'Constant '
83
+ : 'Property ' ;
76
84
77
85
return [
78
86
sprintf (
79
- 'Property fetch %s%s is prohibited on unknown type (%s) ' ,
87
+ '%s fetch %s%s is prohibited on unknown type (%s) ' ,
88
+ $ element ,
80
89
$ this ->getFetchToken ($ node ),
81
- $ property ,
90
+ $ propertyOrConstant ,
82
91
$ this ->printer ->prettyPrintExpr ($ caller ),
83
92
),
84
93
];
@@ -88,11 +97,12 @@ private function processFetch(Node $node, Scope $scope): array
88
97
}
89
98
90
99
/**
91
- * @param PropertyFetch|StaticPropertyFetch $node
100
+ * @param PropertyFetch|StaticPropertyFetch|ClassConstFetch $node
92
101
*/
93
102
private function getFetchToken (Node $ node ): string
94
103
{
95
104
switch (get_class ($ node )) {
105
+ case ClassConstFetch::class:
96
106
case StaticPropertyFetch::class:
97
107
return ':: ' ;
98
108
@@ -104,4 +114,28 @@ private function getFetchToken(Node $node): string
104
114
}
105
115
}
106
116
117
+ /**
118
+ * Detect object::class
119
+ *
120
+ * @param PropertyFetch|StaticPropertyFetch|ClassConstFetch $node
121
+ */
122
+ private function isObjectClassFetch (Type $ callerType , Node $ node ): bool
123
+ {
124
+ $ isObjectWithoutClassName = $ callerType ->isObject ()->yes () && $ callerType ->getObjectClassNames () === [];
125
+
126
+ if (!$ isObjectWithoutClassName ) {
127
+ return false ;
128
+ }
129
+
130
+ if (!$ node instanceof ClassConstFetch) {
131
+ return false ;
132
+ }
133
+
134
+ if (!$ node ->name instanceof Identifier) {
135
+ return false ;
136
+ }
137
+
138
+ return $ node ->name ->name === 'class ' ;
139
+ }
140
+
107
141
}
0 commit comments