Merge pull request #729 from rectorphp/simplify-empty-array-check

[WIP][CodeQuality] Create SimplifyEmptyArrayCheckRector
This commit is contained in:
Gabriel Caruso 2018-10-30 00:38:38 +01:00 committed by GitHub
commit c2ccef23ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 212 additions and 0 deletions

View File

@ -146,6 +146,17 @@ Simplify `in_array` and `array_keys` functions combination into `array_key_exist
+array_key_exists("key", $array);
```
### `SimplifyEmptyArrayCheckRector`
- class: `Rector\CodeQuality\Rector\FuncCall\SimplifyEmptyArrayCheckRector`
Simplify `is_array` and `empty` functions combination into a simple identical check for an empty array
```diff
-is_array($values) && empty($values)
+$values === []
```
### `SimplifyStrposLowerRector`
- class: `Rector\CodeQuality\Rector\FuncCall\SimplifyStrposLowerRector`

View File

@ -0,0 +1,88 @@
<?php declare(strict_types=1);
namespace Rector\CodeQuality\Rector\FuncCall;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\BinaryOp\BooleanAnd;
use PhpParser\Node\Expr\BinaryOp\Identical;
use PhpParser\Node\Expr\BinaryOp\NotIdentical;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Empty_;
use PhpParser\Node\Expr\FuncCall;
use Rector\Rector\AbstractRector;
use Rector\RectorDefinition\CodeSample;
use Rector\RectorDefinition\RectorDefinition;
final class SimplifyEmptyArrayCheckRector extends AbstractRector
{
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'Simplify `is_array` and `empty` functions combination into a simple identical check for an empty array',
[new CodeSample('is_array($values) && empty($values)', '$values === []')]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [BooleanAnd::class];
}
/**
* @param BooleanAnd $booleanAndNode
*/
public function refactor(Node $booleanAndNode): ?Node
{
$leftValue = $booleanAndNode->left;
$rightValue = $booleanAndNode->right;
if (! $this->isPreferableCheck($leftValue, $rightValue)) {
return null;
}
if (! $rightValue instanceof BooleanNot && ! $leftValue instanceof BooleanNot) {
/** @var FuncCall $variable */
$variable = $leftValue instanceof Empty_ ? $leftValue->expr : $rightValue->expr;
return new Identical($variable, new Array_());
}
$booleanNotExpr = $rightValue instanceof BooleanNot ? $rightValue->expr : $leftValue->expr;
if (! $this->isName($booleanNotExpr, 'is_array') && ! $booleanNotExpr instanceof Empty_) {
return null;
}
$variable = $booleanNotExpr->expr ?? $booleanNotExpr;
return new NotIdentical($variable, new Array_());
}
/**
* @param mixed $node
*/
public function isNoneOfPreferableNodes($node): bool
{
return ! $this->isName($node, 'is_array') && ! $node instanceof Empty_ && ! $node instanceof BooleanNot;
}
/**
* @param mixed $leftValue
* @param mixed $rightValue
*/
private function isPreferableCheck($leftValue, $rightValue): bool
{
if ($this->isNoneOfPreferableNodes($leftValue) || $this->isNoneOfPreferableNodes($rightValue)) {
return false;
}
// special case
if ($leftValue instanceof BooleanNot && $rightValue instanceof BooleanNot) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
final class MyClass
{
public function isEmptyArray($values): bool
{
return $values === [];
}
public function isEmptyArray2($values): bool
{
return $values === [];
}
public function isntEmptyArray($values): bool
{
return $values !== [];
}
public function isntEmptyArray2($values): bool
{
return $values !== [];
}
public function twoNegatives($values): bool
{
return ! empty($values->foo) && ! is_array($values->bar);
}
public function functionCallInsideEmpty($values): bool
{
return array_filter($values) === [];
}
}

View File

@ -0,0 +1,6 @@
<?php declare(strict_types=1);
$invalid = is_array($var) && in_array('foo', $var);
$almostValid = is_array($var) && count($var) > 0;
$invalid2 = isset($this->events[$event]) && ! empty($this->events[$event]);
$completelyInvalid = ! $value instanceof Foo && ! $value instanceof Bar;

View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace Rector\CodeQuality\Tests\Rector\FuncCall\SimplifyEmptyArrayCheckRector;
use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
/**
* @covers \Rector\CodeQuality\Rector\FuncCall\SimplifyEmptyArrayCheckRector
*/
final class SimplifyEmptyArrayCheckRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideWrongToFixedFiles()
*/
public function test(string $wrong, string $fixed): void
{
$this->doTestFileMatchesExpectedContent($wrong, $fixed);
}
public function provideWrongToFixedFiles(): Iterator
{
yield [__DIR__ . '/Wrong/wrong.php.inc', __DIR__ . '/Correct/correct.php.inc'];
yield [__DIR__ . '/Wrong/wrong2.php.inc', __DIR__ . '/Correct/correct2.php.inc'];
}
protected function provideConfig(): string
{
return __DIR__ . '/config.yml';
}
}

View File

@ -0,0 +1,34 @@
<?php declare(strict_types=1);
final class MyClass
{
public function isEmptyArray($values): bool
{
return is_array($values) && empty($values);
}
public function isEmptyArray2($values): bool
{
return empty($values) && is_array($values);
}
public function isntEmptyArray($values): bool
{
return is_array($values) && ! empty($values);
}
public function isntEmptyArray2($values): bool
{
return ! empty($values) && is_array($values);
}
public function twoNegatives($values): bool
{
return ! empty($values->foo) && ! is_array($values->bar);
}
public function functionCallInsideEmpty($values): bool
{
return is_array($values) && empty(array_filter($values));
}
}

View File

@ -0,0 +1,6 @@
<?php declare(strict_types=1);
$invalid = is_array($var) && in_array('foo', $var);
$almostValid = is_array($var) && count($var) > 0;
$invalid2 = isset($this->events[$event]) && ! empty($this->events[$event]);
$completelyInvalid = ! $value instanceof Foo && ! $value instanceof Bar;

View File

@ -0,0 +1,2 @@
services:
Rector\CodeQuality\Rector\FuncCall\SimplifyEmptyArrayCheckRector: ~