battleshipTernaryAnalyzer = $battleshipTernaryAnalyzer; $this->valueResolver = $valueResolver; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Changes if/else to spaceship <=> where useful', [new CodeSample(<<<'CODE_SAMPLE' usort($languages, function ($first, $second) { if ($first[0] === $second[0]) { return 0; } return ($first[0] < $second[0]) ? 1 : -1; }); CODE_SAMPLE , <<<'CODE_SAMPLE' usort($languages, function ($first, $second) { return $second[0] <=> $first[0]; }); CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [StmtsAwareInterface::class, If_::class]; } /** * @param StmtsAwareInterface|If_ $node */ public function refactor(Node $node) : ?Node { if ($node instanceof If_) { return $this->refactorIf($node); } if ($node->stmts === null) { return null; } foreach ($node->stmts as $key => $stmt) { if (!$stmt instanceof Return_) { continue; } if (!$stmt->expr instanceof Ternary) { continue; } // preceeded by if $prevStmt = $node->stmts[$key - 1] ?? null; if (!$prevStmt instanceof If_) { continue; } $comparedExprs = $this->matchExprComparedExprsReturnZero($prevStmt); if (!$comparedExprs instanceof ComparedExprs) { continue; } $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne($stmt->expr, $comparedExprs); $returnSpaceship = $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); if (!$returnSpaceship instanceof Return_) { continue; } unset($node->stmts[$key - 1]); $node->stmts[$key] = $returnSpaceship; return $node; } return null; } public function provideMinPhpVersion() : int { return PhpVersionFeature::SPACESHIP; } private function refactorIf(If_ $if) : ?Return_ { if ($if->elseifs !== []) { return null; } if (!$if->else instanceof Else_) { return null; } $comparedExprs = $this->matchExprComparedExprsReturnZero($if); if (!$comparedExprs instanceof ComparedExprs) { return null; } $ternary = $this->matchElseOnlyStmtTernary($if->else); if (!$ternary instanceof Ternary) { return null; } $battleshipCompareOrder = $this->battleshipTernaryAnalyzer->isGreaterLowerCompareReturnOneAndMinusOne($ternary, $comparedExprs); return $this->createReturnSpaceship($battleshipCompareOrder, $comparedExprs); } /** * We look for: * * if ($firstValue === $secondValue) { * return 0; * } */ private function matchExprComparedExprsReturnZero(If_ $if) : ?ComparedExprs { if (!$if->cond instanceof Equal && !$if->cond instanceof Identical) { return null; } $binaryOp = $if->cond; if (\count($if->stmts) !== 1) { return null; } $onlyStmt = $if->stmts[0]; if (!$onlyStmt instanceof Return_) { return null; } if (!$onlyStmt->expr instanceof Expr) { return null; } if (!$this->valueResolver->isValue($onlyStmt->expr, 0)) { return null; } return new ComparedExprs($binaryOp->left, $binaryOp->right); } /** * @param BattleshipCompareOrder::*|null $battleshipCompareOrder */ private function createReturnSpaceship(?string $battleshipCompareOrder, ComparedExprs $comparedExprs) : ?Return_ { if ($battleshipCompareOrder === null) { return null; } if ($battleshipCompareOrder === BattleshipCompareOrder::DESC) { $spaceship = new Spaceship($comparedExprs->getFirstExpr(), $comparedExprs->getSecondExpr()); } else { $spaceship = new Spaceship($comparedExprs->getSecondExpr(), $comparedExprs->getFirstExpr()); } return new Return_($spaceship); } private function matchElseOnlyStmtTernary(Else_ $else) : ?\PhpParser\Node\Expr\Ternary { if (\count($else->stmts) !== 1) { return null; } $onlyElseStmt = $else->stmts[0]; if (!$onlyElseStmt instanceof Return_) { return null; } if (!$onlyElseStmt->expr instanceof Ternary) { return null; } return $onlyElseStmt->expr; } }