Skip to content

Commit bf42256

Browse files
committed
feat: add support for converting is_null ternaries to null coalescing operator
1 parent 6a31016 commit bf42256

2 files changed

Lines changed: 75 additions & 0 deletions

File tree

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace Rector\Tests\Php70\Rector\Ternary\TernaryToNullCoalescingRector\Fixture;
4+
5+
function ternaryWithIsNull()
6+
{
7+
$x = is_null($value) ? 10 : $value;
8+
9+
$y = is_null($a) ? $b : $a;
10+
11+
$z = !is_null($value) ? $value : 10;
12+
13+
$w = !is_null($c) ? $c : $d;
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\Php70\Rector\Ternary\TernaryToNullCoalescingRector\Fixture;
21+
22+
function ternaryWithIsNull()
23+
{
24+
$x = $value ?? 10;
25+
26+
$y = $a ?? $b;
27+
28+
$z = $value ?? 10;
29+
30+
$w = $c ?? $d;
31+
}
32+
33+
?>
34+

rules/Php70/Rector/Ternary/TernaryToNullCoalescingRector.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use PhpParser\Node\Expr\BinaryOp\Coalesce;
1010
use PhpParser\Node\Expr\BinaryOp\Identical;
1111
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
12+
use PhpParser\Node\Expr\BooleanNot;
13+
use PhpParser\Node\Expr\FuncCall;
1214
use PhpParser\Node\Expr\Isset_;
1315
use PhpParser\Node\Expr\Ternary;
1416
use Rector\PhpParser\Node\Value\ValueResolver;
@@ -35,6 +37,7 @@ public function getRuleDefinition(): RuleDefinition
3537
[
3638
new CodeSample('$value === null ? 10 : $value;', '$value ?? 10;'),
3739
new CodeSample('isset($value) ? $value : 10;', '$value ?? 10;'),
40+
new CodeSample('is_null($value) ? 10 : $value;', '$value ?? 10;'),
3841
]
3942
);
4043
}
@@ -56,6 +59,17 @@ public function refactor(Node $node): ?Node
5659
return $this->processTernaryWithIsset($node, $node->cond);
5760
}
5861

62+
if ($node->cond instanceof FuncCall && $this->isName($node->cond, 'is_null')) {
63+
return $this->processTernaryWithIsNull($node, $node->cond, false);
64+
}
65+
66+
if (
67+
$node->cond instanceof BooleanNot && $node->cond->expr instanceof FuncCall
68+
&& $this->isName($node->cond->expr, 'is_null')
69+
) {
70+
return $this->processTernaryWithIsNull($node, $node->cond->expr, true);
71+
}
72+
5973
if ($node->cond instanceof Identical) {
6074
$checkedNode = $node->else;
6175
$fallbackNode = $node->if;
@@ -93,6 +107,33 @@ public function provideMinPhpVersion(): int
93107
return PhpVersionFeature::NULL_COALESCE;
94108
}
95109

110+
private function processTernaryWithIsNull(Ternary $ternary, FuncCall $isNullFuncCall, bool $isNegated): ?Coalesce
111+
{
112+
if (count($isNullFuncCall->args) !== 1) {
113+
return null;
114+
}
115+
116+
$checkedExpr = $isNullFuncCall->args[0]->value;
117+
118+
if ($isNegated) {
119+
if (! $ternary->if instanceof Expr) {
120+
return null;
121+
}
122+
123+
if (! $this->nodeComparator->areNodesEqual($ternary->if, $checkedExpr)) {
124+
return null;
125+
}
126+
127+
return new Coalesce($ternary->if, $ternary->else);
128+
}
129+
130+
if (! $this->nodeComparator->areNodesEqual($ternary->else, $checkedExpr)) {
131+
return null;
132+
}
133+
134+
return new Coalesce($ternary->else, $ternary->if);
135+
}
136+
96137
private function processTernaryWithIsset(Ternary $ternary, Isset_ $isset): ?Coalesce
97138
{
98139
if (! $ternary->if instanceof Expr) {

0 commit comments

Comments
 (0)