11
11
use PHPStan \Analyser \Scope ;
12
12
use PHPStan \Reflection \ReflectionProvider ;
13
13
use PHPStan \Rules \Rule ;
14
- use PHPStan \Type \CallableType ;
15
- use PHPStan \Type \MixedType ;
16
14
use PHPStan \Type \ObjectType ;
17
- use PHPStan \Type \ObjectWithoutClassType ;
15
+ use PHPStan \Type \Type ;
16
+ use PHPStan \Type \TypeUtils ;
18
17
use PHPStan \Type \VerbosityLevel ;
19
18
use function count ;
20
19
@@ -25,11 +24,6 @@ class ForbidIdenticalClassComparisonRule implements Rule
25
24
{
26
25
27
26
private const DEFAULT_BLACKLIST = [DateTimeInterface::class];
28
- private const IGNORED_TYPES = [
29
- MixedType::class, // mixed is "maybe" accepted by any (denied) class
30
- ObjectWithoutClassType::class, // object is "maybe" accepted by any (denied) class
31
- CallableType::class, // any non-final class descendant can have __invoke method causing it to be "maybe" accepted by any (denied) class
32
- ];
33
27
34
28
/**
35
29
* @var array<int, class-string<object>>
@@ -80,20 +74,14 @@ public function processNode(Node $node, Scope $scope): array
80
74
return []; // always-true or always-false, already reported by native PHPStan (like $a === $a)
81
75
}
82
76
83
- foreach (self ::IGNORED_TYPES as $ ignoredType ) {
84
- if ($ leftType instanceof $ ignoredType || $ rightType instanceof $ ignoredType ) {
85
- return [];
86
- }
87
- }
88
-
89
77
$ errors = [];
90
78
91
79
foreach ($ this ->blacklist as $ className ) {
92
80
$ forbiddenObjectType = new ObjectType ($ className );
93
81
94
82
if (
95
- ! $ forbiddenObjectType -> accepts ($ leftType , $ scope -> isDeclareStrictTypes ())-> no ( )
96
- && ! $ forbiddenObjectType -> accepts ($ rightType , $ scope -> isDeclareStrictTypes ())-> no ( )
83
+ $ this -> containsClass ($ leftType , $ className )
84
+ && $ this -> containsClass ($ rightType , $ className )
97
85
) {
98
86
$ errors [] = "Using {$ node ->getOperatorSigil ()} with {$ forbiddenObjectType ->describe (VerbosityLevel::typeOnly ())} is denied " ;
99
87
}
@@ -102,4 +90,19 @@ public function processNode(Node $node, Scope $scope): array
102
90
return $ errors ;
103
91
}
104
92
93
+ private function containsClass (Type $ type , string $ className ): bool
94
+ {
95
+ $ benevolentType = TypeUtils::toBenevolentUnion ($ type );
96
+
97
+ foreach ($ benevolentType ->getObjectClassNames () as $ classNameInType ) {
98
+ $ classInType = new ObjectType ($ classNameInType );
99
+
100
+ if ($ classInType ->isInstanceOf ($ className )->yes ()) {
101
+ return true ;
102
+ }
103
+ }
104
+
105
+ return false ;
106
+ }
107
+
105
108
}
0 commit comments