[PHP 7.2] Add ReplaceEachAssignmentWithKeyCurrentRector

This commit is contained in:
TomasVotruba 2020-06-17 14:12:29 +02:00
parent 4754343746
commit 3744649dd9
9 changed files with 334 additions and 2 deletions

View File

@ -1,6 +1,8 @@
services:
Rector\Php72\Rector\Each\WhileEachToForeachRector: null
Rector\Php72\Rector\Each\ListEachRector: null
Rector\Php72\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector: null
Rector\Php72\Rector\Unset_\UnsetCastRector: null
Rector\Php72\Rector\ConstFetch\BarewordStringRector: null

View File

@ -1,4 +1,4 @@
# All 511 Rectors Overview
# All 512 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -45,7 +45,7 @@
- [Php56](#php56) (2)
- [Php70](#php70) (18)
- [Php71](#php71) (9)
- [Php72](#php72) (10)
- [Php72](#php72) (11)
- [Php73](#php73) (10)
- [Php74](#php74) (15)
- [Php80](#php80) (10)
@ -7815,6 +7815,25 @@ Use $result argument in `parse_str()` function
<br><br>
### `ReplaceEachAssignmentWithKeyCurrentRector`
- class: [`Rector\Php72\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector`](/../master/rules/php72/src/Rector/Each/ReplaceEachAssignmentWithKeyCurrentRector.php)
- [test fixtures](/../master/rules/php72/tests/Rector/Each/ReplaceEachAssignmentWithKeyCurrentRector/Fixture)
Replace `each()` assign outside loop
```diff
$array = ['b' => 1, 'a' => 2];
-$eachedArray = each($array);
+$eachedArray[1] = current($array);
+$eachedArray['value'] = current($array);
+$eachedArray[0] = key($array);
+$eachedArray['key'] = key($array);
+next($array);
```
<br><br>
### `StringifyDefineRector`
- class: [`Rector\Php72\Rector\FuncCall\StringifyDefineRector`](/../master/rules/php72/src/Rector/FuncCall/StringifyDefineRector.php)

View File

@ -86,6 +86,16 @@ trait NodeCommandersTrait
$this->useNodesToAddCollector->addUseImport($positionNode, $objectType);
}
/**
* @param Node[] $newNodes
*/
protected function addNodesAfterNode(array $newNodes, Node $positionNode): void
{
foreach ($newNodes as $newNode) {
$this->addNodeAfterNode($newNode, $positionNode);
}
}
protected function addNodeAfterNode(Node $newNode, Node $positionNode): void
{
$this->nodesToAddCollector->addNodeAfterNode($newNode, $positionNode);

View File

@ -0,0 +1,127 @@
<?php
declare(strict_types=1);
namespace Rector\Php72\Rector\Each;
use PhpParser\BuilderHelpers;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\List_;
use PhpParser\Node\Stmt\While_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
/**
* @see \Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\ReplaceEachAssignmentWithKeyCurrentRectorTest
*/
final class ReplaceEachAssignmentWithKeyCurrentRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Replace each() assign outside loop', [
new CodeSample(
<<<'CODE_SAMPLE'
$array = ['b' => 1, 'a' => 2];
$eachedArray = each($array);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
$array = ['b' => 1, 'a' => 2];
$eachedArray[1] = current($array);
$eachedArray['value'] = current($array);
$eachedArray[0] = key($array);
$eachedArray['key'] = key($array);
next($array);
CODE_SAMPLE
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
/** @var FuncCall $eachFuncCall */
$eachFuncCall = $node->expr;
$eachedVariable = $eachFuncCall->args[0]->value;
$assignVariable = $node->var;
$newNodes = $this->createNewNodes($assignVariable, $eachedVariable);
$this->addNodesAfterNode($newNodes, $node);
$this->removeNode($node);
return null;
}
private function shouldSkip(Assign $assign): bool
{
if (! $this->isFuncCallName($assign->expr, 'each')) {
return true;
}
$parentNode = $assign->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof While_) {
return true;
}
// skip assign to List
if ($parentNode instanceof Assign && $parentNode->var instanceof List_) {
return true;
}
return false;
}
/**
* @return Node[]
*/
private function createNewNodes(Expr $assignVariable, Expr $eachedVariable): array
{
$newNodes = [];
$newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 1, 'current');
$newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 'value', 'current');
$newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 0, 'key');
$newNodes[] = $this->createDimFetchAssignWithFuncCall($assignVariable, $eachedVariable, 'key', 'key');
$newNodes[] = $this->createFuncCall('next', [new Arg($eachedVariable)]);
return $newNodes;
}
/**
* @param string|int $dimValue
*/
private function createDimFetchAssignWithFuncCall(
Expr $assignVariable,
Expr $eachedVariable,
$dimValue,
string $functionName
): Assign {
$dim = BuilderHelpers::normalizeValue($dimValue);
$arrayDimFetch = new ArrayDimFetch($assignVariable, $dim);
return new Assign($arrayDimFetch, $this->createFuncCall($functionName, [new Arg($eachedVariable)]));
}
}

View File

@ -0,0 +1,48 @@
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
use Rector\Core\Testing\Contract\RunnableInterface;
final class ComparedResults implements RunnableInterface
{
public function run()
{
$array = ['a' => 1, 'b' => 2];
$first = each($array);
$second = each($array);
return [$first, $second];
}
}
?>
-----
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
use Rector\Core\Testing\Contract\RunnableInterface;
final class ComparedResults implements RunnableInterface
{
public function run()
{
$array = ['a' => 1, 'b' => 2];
$first[1] = current($array);
$first['value'] = current($array);
$first[0] = key($array);
$first['key'] = key($array);
next($array);
$second[1] = current($array);
$second['value'] = current($array);
$second[0] = key($array);
$second['key'] = key($array);
next($array);
return [$first, $second];
}
}
?>

View File

@ -0,0 +1,42 @@
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
use Rector\Core\Testing\Contract\RunnableInterface;
class EachFixture implements RunnableInterface
{
public function run()
{
$array = ['b' => 1, 'a' => 2];
$old = each($array);
return $old;
}
}
?>
-----
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
use Rector\Core\Testing\Contract\RunnableInterface;
class EachFixture implements RunnableInterface
{
public function run()
{
$array = ['b' => 1, 'a' => 2];
$old[1] = current($array);
$old['value'] = current($array);
$old[0] = key($array);
$old['key'] = key($array);
next($array);
return $old;
}
}
?>

View File

@ -0,0 +1,34 @@
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
final class OldAssignment
{
public function run()
{
$array = ['a' => 1, 'b' => 2];
$old = each($array);
}
}
?>
-----
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
final class OldAssignment
{
public function run()
{
$array = ['a' => 1, 'b' => 2];
$old[1] = current($array);
$old['value'] = current($array);
$old[0] = key($array);
$old['key'] = key($array);
next($array);
}
}
?>

View File

@ -0,0 +1,16 @@
<?php
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector\Fixture;
final class SkipWhile
{
public function run()
{
$array = ['a' => 1, 'b' => 2];
$list = [];
while ($first = each($array)) {
$list[] = $first;
}
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Rector\Php72\Tests\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Php72\Rector\Each\ReplaceEachAssignmentWithKeyCurrentRector;
use SplFileInfo;
final class ReplaceEachAssignmentWithKeyCurrentRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideDataForTest()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
/**
* @return Iterator<SplFileInfo>
*/
public function provideDataForTest(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return ReplaceEachAssignmentWithKeyCurrentRector::class;
}
}