77use PhpParser \Node ;
88use PhpParser \Node \Expr ;
99use PhpParser \Node \Expr \Assign ;
10+ use PhpParser \Node \Expr \BinaryOp ;
1011use PhpParser \Node \Expr \BinaryOp \BooleanAnd ;
1112use PhpParser \Node \Expr \BinaryOp \BooleanOr ;
1213use PhpParser \Node \Expr \BooleanNot ;
14+ use PhpParser \Node \Expr \Empty_ ;
1315use PhpParser \Node \Expr \Instanceof_ ;
16+ use PhpParser \Node \Expr \Isset_ ;
1417use PhpParser \Node \Name \FullyQualified ;
1518use PHPStan \Type \ObjectType ;
1619use Rector \Rector \AbstractRector ;
@@ -89,7 +92,7 @@ public function refactor(Node $node): ?Node
8992 */
9093 private function processNullableInstance (BooleanAnd |BooleanOr $ node ): null |BooleanAnd |BooleanOr
9194 {
92- $ nullableObjectType = $ this ->nullableTypeAnalyzer -> resolveNullableObjectType ($ node ->left );
95+ $ nullableObjectType = $ this ->resolveNullableObjectType ($ node ->left );
9396
9497 $ hasChanged = false ;
9598 if ($ nullableObjectType instanceof ObjectType) {
@@ -98,7 +101,7 @@ private function processNullableInstance(BooleanAnd|BooleanOr $node): null|Boole
98101 $ hasChanged = true ;
99102 }
100103
101- $ nullableObjectType = $ this ->nullableTypeAnalyzer -> resolveNullableObjectType ($ node ->right );
104+ $ nullableObjectType = $ this ->resolveNullableObjectType ($ node ->right );
102105
103106 if ($ nullableObjectType instanceof ObjectType) {
104107 $ node ->right = $ this ->createExprInstanceof ($ node ->right , $ nullableObjectType );
@@ -117,7 +120,7 @@ private function processNegationBooleanOr(BooleanOr $booleanOr): ?BooleanOr
117120 {
118121 $ hasChanged = false ;
119122 if ($ booleanOr ->left instanceof BooleanNot) {
120- $ nullableObjectType = $ this ->nullableTypeAnalyzer -> resolveNullableObjectType ($ booleanOr ->left ->expr );
123+ $ nullableObjectType = $ this ->resolveNullableObjectType ($ booleanOr ->left ->expr );
121124
122125 if ($ nullableObjectType instanceof ObjectType) {
123126 $ booleanOr ->left ->expr = $ this ->createExprInstanceof ($ booleanOr ->left ->expr , $ nullableObjectType );
@@ -126,7 +129,7 @@ private function processNegationBooleanOr(BooleanOr $booleanOr): ?BooleanOr
126129 }
127130
128131 if ($ booleanOr ->right instanceof BooleanNot) {
129- $ nullableObjectType = $ this ->nullableTypeAnalyzer -> resolveNullableObjectType ($ booleanOr ->right ->expr );
132+ $ nullableObjectType = $ this ->resolveNullableObjectType ($ booleanOr ->right ->expr );
130133
131134 if ($ nullableObjectType instanceof ObjectType) {
132135 $ booleanOr ->right ->expr = $ this ->createExprInstanceof ($ booleanOr ->right ->expr , $ nullableObjectType );
@@ -142,6 +145,26 @@ private function processNegationBooleanOr(BooleanOr $booleanOr): ?BooleanOr
142145 return $ this ->processNullableInstance ($ booleanOr );
143146 }
144147
148+ private function resolveNullableObjectType (Expr $ expr ): ?ObjectType
149+ {
150+ // cheap bail - bool/scalar-shaped operands can never be a nullable object,
151+ // skip the costly native type resolution for them (e.g. chained "$a && $b && $c")
152+ if ($ this ->isNeverNullableObject ($ expr )) {
153+ return null ;
154+ }
155+
156+ return $ this ->nullableTypeAnalyzer ->resolveNullableObjectType ($ expr );
157+ }
158+
159+ private function isNeverNullableObject (Expr $ expr ): bool
160+ {
161+ return $ expr instanceof BinaryOp
162+ || $ expr instanceof BooleanNot
163+ || $ expr instanceof Instanceof_
164+ || $ expr instanceof Empty_
165+ || $ expr instanceof Isset_;
166+ }
167+
145168 private function createExprInstanceof (Expr $ expr , ObjectType $ objectType ): Instanceof_
146169 {
147170 $ fullyQualified = new FullyQualified ($ objectType ->getClassName ());
0 commit comments