rector/packages/Php/src/Rector/Each/ListEachRector.php

142 lines
4.2 KiB
PHP
Raw Normal View History

2018-10-03 13:14:08 +00:00
<?php declare(strict_types=1);
namespace Rector\Php\Rector\Each;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Stmt\Do_;
use PhpParser\Node\Stmt\Expression;
use Rector\NodeTypeResolver\Node\Attribute;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
/**
* @source https://wiki.php.net/rfc/deprecations_php_7_2#each
*/
final class ListEachRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'each() function is deprecated, use foreach() instead.',
[
new CodeSample(
<<<'CODE_SAMPLE'
list($key, $callback) = each($callbacks);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
$key = key($opt->option);
$val = current($opt->option);
CODE_SAMPLE
),
]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
2018-10-03 13:14:08 +00:00
*/
public function refactor(Node $node): ?Node
2018-10-03 13:14:08 +00:00
{
if ($this->shouldSkip($node)) {
return null;
2018-10-03 13:14:08 +00:00
}
/** @var List_ $listNode */
$listNode = $node->var;
2018-10-03 13:14:08 +00:00
/** @var FuncCall $eachFuncCall */
$eachFuncCall = $node->expr;
2018-10-03 13:14:08 +00:00
// only key: list($key, ) = each($values);
if ($listNode->items[0] && $listNode->items[1] === null) {
$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
2018-10-03 13:14:08 +00:00
return new Assign($listNode->items[0]->value, $keyFuncCall);
}
// only value: list(, $value) = each($values);
if ($listNode->items[1] && $listNode->items[0] === null) {
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);
2018-10-03 13:14:08 +00:00
$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
2018-10-03 13:14:08 +00:00
return new Assign($listNode->items[1]->value, $currentFuncCall);
}
// both: list($key, $value) = each($values);
// ↓
// $key = key($values);
// $value = current($values);
// next($values); - only inside a loop
$currentFuncCall = $this->createFunction('current', $eachFuncCall->args);
2018-10-07 09:53:59 +00:00
$assignCurrentNode = new Assign($listNode->items[1]->value, $currentFuncCall);
$this->addNodeAfterNode($assignCurrentNode, $node);
2018-10-03 13:14:08 +00:00
if ($this->isInsideDoWhile($node)) {
$nextFuncCall = $this->createFunction('next', $eachFuncCall->args);
$this->addNodeAfterNode($nextFuncCall, $node);
2018-10-03 13:14:08 +00:00
}
$keyFuncCall = $this->createFunction('key', $eachFuncCall->args);
2018-10-07 09:53:59 +00:00
return new Assign($listNode->items[0]->value, $keyFuncCall);
2018-10-03 13:14:08 +00:00
}
2018-10-31 15:34:37 +00:00
private function shouldSkip(Assign $assignNode): bool
2018-10-03 13:14:08 +00:00
{
2018-10-31 15:34:37 +00:00
if (! $this->isListToEachAssign($assignNode)) {
return true;
2018-10-03 13:14:08 +00:00
}
2018-10-31 15:34:37 +00:00
// assign should be top level, e.g. not in a while loop
if (! $assignNode->getAttribute(Attribute::PARENT_NODE) instanceof Expression) {
return true;
}
/** @var List_ $listNode */
$listNode = $assignNode->var;
if (count($listNode->items) !== 2) {
return true;
}
// empty list → cannot handle
return $listNode->items[0] === null && $listNode->items[1] === null;
2018-10-03 13:14:08 +00:00
}
/**
* Is inside the "do {} while ();" loop need to add "next()"
*/
private function isInsideDoWhile(Node $assignNode): bool
{
$parentNode = $assignNode->getAttribute(Attribute::PARENT_NODE);
if (! $parentNode instanceof Expression) {
return false;
}
$parentParentNode = $parentNode->getAttribute(Attribute::PARENT_NODE);
return $parentParentNode instanceof Do_;
}
2018-10-07 09:53:59 +00:00
2018-10-31 15:34:37 +00:00
private function isListToEachAssign(Assign $assignNode): bool
2018-10-07 09:53:59 +00:00
{
2018-10-31 15:34:37 +00:00
if (! $assignNode->var instanceof List_) {
return false;
2018-10-07 09:53:59 +00:00
}
2018-10-31 15:34:37 +00:00
return $assignNode->expr instanceof FuncCall && $this->isName($assignNode->expr, 'each');
2018-10-07 09:53:59 +00:00
}
2018-10-03 13:14:08 +00:00
}