2021-06-07 21:47:57 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare (strict_types=1);
|
|
|
|
namespace Rector\Php80\NodeAnalyzer;
|
|
|
|
|
2022-05-19 16:31:50 +00:00
|
|
|
use PhpParser\Node;
|
|
|
|
use PhpParser\Node\Expr;
|
2021-06-07 21:47:57 +00:00
|
|
|
use PhpParser\Node\Expr\ArrayDimFetch;
|
|
|
|
use PhpParser\Node\Expr\Assign;
|
2021-06-08 11:02:16 +00:00
|
|
|
use PhpParser\Node\Expr\Match_;
|
2022-05-19 16:31:50 +00:00
|
|
|
use PhpParser\Node\Stmt\Expression;
|
2021-06-08 11:02:16 +00:00
|
|
|
use PhpParser\Node\Stmt\Return_;
|
2021-06-07 21:47:57 +00:00
|
|
|
use PhpParser\Node\Stmt\Switch_;
|
2021-06-08 13:06:20 +00:00
|
|
|
use PhpParser\Node\Stmt\Throw_;
|
2022-05-19 16:31:50 +00:00
|
|
|
use Rector\Core\PhpParser\Comparing\NodeComparator;
|
2021-06-07 21:47:57 +00:00
|
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
2021-06-08 11:02:16 +00:00
|
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
2021-06-28 21:18:55 +00:00
|
|
|
use Rector\Php80\Enum\MatchKind;
|
2021-06-07 21:47:57 +00:00
|
|
|
use Rector\Php80\ValueObject\CondAndExpr;
|
|
|
|
final class MatchSwitchAnalyzer
|
|
|
|
{
|
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-06-07 21:47:57 +00:00
|
|
|
* @var \Rector\Php80\NodeAnalyzer\SwitchAnalyzer
|
|
|
|
*/
|
|
|
|
private $switchAnalyzer;
|
|
|
|
/**
|
2021-12-04 12:47:17 +00:00
|
|
|
* @readonly
|
2021-06-07 21:47:57 +00:00
|
|
|
* @var \Rector\NodeNameResolver\NodeNameResolver
|
|
|
|
*/
|
|
|
|
private $nodeNameResolver;
|
2022-05-19 16:31:50 +00:00
|
|
|
/**
|
|
|
|
* @readonly
|
|
|
|
* @var \Rector\Core\PhpParser\Comparing\NodeComparator
|
|
|
|
*/
|
|
|
|
private $nodeComparator;
|
2022-05-27 11:51:31 +00:00
|
|
|
public function __construct(\Rector\Php80\NodeAnalyzer\SwitchAnalyzer $switchAnalyzer, \Rector\NodeNameResolver\NodeNameResolver $nodeNameResolver, \Rector\Core\PhpParser\Comparing\NodeComparator $nodeComparator)
|
2021-06-07 21:47:57 +00:00
|
|
|
{
|
|
|
|
$this->switchAnalyzer = $switchAnalyzer;
|
|
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
2022-05-19 16:31:50 +00:00
|
|
|
$this->nodeComparator = $nodeComparator;
|
2021-06-07 21:47:57 +00:00
|
|
|
}
|
2021-06-07 23:07:01 +00:00
|
|
|
/**
|
|
|
|
* @param CondAndExpr[] $condAndExprs
|
|
|
|
*/
|
2022-05-27 11:51:31 +00:00
|
|
|
public function shouldSkipSwitch(\PhpParser\Node\Stmt\Switch_ $switch, array $condAndExprs) : bool
|
2021-06-07 21:47:57 +00:00
|
|
|
{
|
2021-06-07 23:07:01 +00:00
|
|
|
if ($condAndExprs === []) {
|
|
|
|
return \true;
|
|
|
|
}
|
2021-06-07 21:47:57 +00:00
|
|
|
if (!$this->switchAnalyzer->hasEachCaseBreak($switch)) {
|
|
|
|
return \true;
|
|
|
|
}
|
2022-04-24 15:27:24 +00:00
|
|
|
if ($this->switchAnalyzer->hasDifferentTypeCases($switch->cases)) {
|
|
|
|
return \true;
|
|
|
|
}
|
2021-06-07 21:47:57 +00:00
|
|
|
if (!$this->switchAnalyzer->hasEachCaseSingleStmt($switch)) {
|
|
|
|
return \false;
|
|
|
|
}
|
2022-03-31 11:04:24 +00:00
|
|
|
if ($this->switchAnalyzer->hasDefaultSingleStmt($switch)) {
|
2021-06-08 11:02:16 +00:00
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
// is followed by return? is considered implicit default
|
2021-06-08 13:06:20 +00:00
|
|
|
if ($this->isNextStmtReturnWithExpr($switch)) {
|
|
|
|
return \false;
|
2021-06-08 11:02:16 +00:00
|
|
|
}
|
2021-06-08 13:06:20 +00:00
|
|
|
return !$this->isNextStmtThrows($switch);
|
2021-06-07 21:47:57 +00:00
|
|
|
}
|
|
|
|
/**
|
|
|
|
* @param CondAndExpr[] $condAndExprs
|
|
|
|
*/
|
|
|
|
public function haveCondAndExprsMatchPotential(array $condAndExprs) : bool
|
|
|
|
{
|
2021-06-07 23:07:01 +00:00
|
|
|
$uniqueCondAndExprKinds = $this->resolveUniqueKindsWithoutThrows($condAndExprs);
|
2021-06-07 21:47:57 +00:00
|
|
|
if (\count($uniqueCondAndExprKinds) > 1) {
|
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
$assignVariableNames = [];
|
|
|
|
foreach ($condAndExprs as $condAndExpr) {
|
|
|
|
$expr = $condAndExpr->getExpr();
|
2022-05-27 11:51:31 +00:00
|
|
|
if (!$expr instanceof \PhpParser\Node\Expr\Assign) {
|
2021-06-07 21:47:57 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-05-27 11:51:31 +00:00
|
|
|
if ($expr->var instanceof \PhpParser\Node\Expr\ArrayDimFetch) {
|
2021-06-07 21:47:57 +00:00
|
|
|
$arrayDimFethName = $this->nodeNameResolver->getName($expr->var->var);
|
|
|
|
$assignVariableNames[] = \get_class($expr->var) . $arrayDimFethName . '[]';
|
|
|
|
} else {
|
|
|
|
$assignVariableNames[] = \get_class($expr->var) . $this->nodeNameResolver->getName($expr->var);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$assignVariableNames = \array_unique($assignVariableNames);
|
|
|
|
return \count($assignVariableNames) <= 1;
|
|
|
|
}
|
2022-05-27 11:51:31 +00:00
|
|
|
public function hasDefaultValue(\PhpParser\Node\Expr\Match_ $match) : bool
|
2021-06-08 11:02:16 +00:00
|
|
|
{
|
|
|
|
foreach ($match->arms as $matchArm) {
|
|
|
|
if ($matchArm->conds === null) {
|
|
|
|
return \true;
|
|
|
|
}
|
|
|
|
if ($matchArm->conds === []) {
|
|
|
|
return \true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return \false;
|
|
|
|
}
|
2021-06-07 21:47:57 +00:00
|
|
|
/**
|
|
|
|
* @param CondAndExpr[] $condAndExprs
|
2021-06-28 21:18:55 +00:00
|
|
|
* @return MatchKind[]
|
2021-06-07 21:47:57 +00:00
|
|
|
*/
|
2021-06-07 23:07:01 +00:00
|
|
|
private function resolveUniqueKindsWithoutThrows(array $condAndExprs) : array
|
2021-06-07 21:47:57 +00:00
|
|
|
{
|
|
|
|
$condAndExprKinds = [];
|
|
|
|
foreach ($condAndExprs as $condAndExpr) {
|
2022-05-27 11:51:31 +00:00
|
|
|
if ($condAndExpr->equalsMatchKind(\Rector\Php80\Enum\MatchKind::THROW())) {
|
2021-06-07 23:07:01 +00:00
|
|
|
continue;
|
|
|
|
}
|
2021-06-28 21:18:55 +00:00
|
|
|
$condAndExprKinds[] = $condAndExpr->getMatchKind();
|
2021-06-07 21:47:57 +00:00
|
|
|
}
|
|
|
|
return \array_unique($condAndExprKinds);
|
|
|
|
}
|
2022-05-27 11:51:31 +00:00
|
|
|
private function isNextStmtReturnWithExpr(\PhpParser\Node\Stmt\Switch_ $switch) : bool
|
2021-06-08 13:06:20 +00:00
|
|
|
{
|
2022-05-27 11:51:31 +00:00
|
|
|
$next = $switch->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::NEXT_NODE);
|
|
|
|
if (!$next instanceof \PhpParser\Node\Stmt\Return_) {
|
2021-06-08 13:06:20 +00:00
|
|
|
return \false;
|
|
|
|
}
|
2022-05-27 11:51:31 +00:00
|
|
|
if (!$next->expr instanceof \PhpParser\Node\Expr) {
|
2022-05-19 16:31:50 +00:00
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
foreach ($switch->cases as $case) {
|
|
|
|
/** @var Expression[] $expressions */
|
2022-05-27 11:51:31 +00:00
|
|
|
$expressions = \array_filter($case->stmts, function (\PhpParser\Node $node) : bool {
|
|
|
|
return $node instanceof \PhpParser\Node\Stmt\Expression;
|
2022-05-19 16:31:50 +00:00
|
|
|
});
|
|
|
|
foreach ($expressions as $expression) {
|
2022-05-27 11:51:31 +00:00
|
|
|
if (!$expression->expr instanceof \PhpParser\Node\Expr\Assign) {
|
2022-05-19 16:31:50 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!$this->nodeComparator->areNodesEqual($expression->expr->var, $next->expr)) {
|
|
|
|
return \false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return \true;
|
2021-06-08 13:06:20 +00:00
|
|
|
}
|
2022-05-27 11:51:31 +00:00
|
|
|
private function isNextStmtThrows(\PhpParser\Node\Stmt\Switch_ $switch) : bool
|
2021-06-08 13:06:20 +00:00
|
|
|
{
|
2022-05-27 11:51:31 +00:00
|
|
|
$next = $switch->getAttribute(\Rector\NodeTypeResolver\Node\AttributeKey::NEXT_NODE);
|
|
|
|
return $next instanceof \PhpParser\Node\Stmt\Throw_;
|
2021-06-08 13:06:20 +00:00
|
|
|
}
|
2021-06-07 21:47:57 +00:00
|
|
|
}
|