rector/rules/php71/src/Rector/Assign/AssignArrayToStringRector.php

181 lines
5.1 KiB
PHP
Raw Normal View History

2019-10-13 05:59:52 +00:00
<?php
declare(strict_types=1);
2018-10-09 10:18:42 +00:00
namespace Rector\Php71\Rector\Assign;
2018-10-09 10:18:42 +00:00
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Cast\Array_ as ArrayCast;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\StaticPropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\PropertyProperty;
2019-01-18 02:41:58 +00:00
use PHPStan\Type\ArrayType;
use PHPStan\Type\Constant\ConstantStringType;
2019-03-10 23:47:43 +00:00
use PHPStan\Type\ErrorType;
2019-01-18 02:41:58 +00:00
use PHPStan\Type\MixedType;
use PHPStan\Type\StringType;
use PHPStan\Type\UnionType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
2018-10-09 10:18:42 +00:00
/**
* @see https://3v4l.org/ABDNv
* @see https://stackoverflow.com/a/41000866/1348344
* @see \Rector\Php71\Tests\Rector\Assign\AssignArrayToStringRector\AssignArrayToStringRectorTest
2018-10-09 10:18:42 +00:00
*/
final class AssignArrayToStringRector extends AbstractRector
{
/**
2018-10-31 11:53:59 +00:00
* @var PropertyProperty[]
2018-10-09 10:18:42 +00:00
*/
2018-10-31 11:53:59 +00:00
private $emptyStringPropertyNodes = [];
2018-10-09 10:18:42 +00:00
public function getDefinition(): RectorDefinition
{
return new RectorDefinition(
'String cannot be turned into array by assignment anymore',
2019-09-18 10:16:31 +00:00
[new CodeSample(<<<'PHP'
2018-10-09 10:18:42 +00:00
$string = '';
$string[] = 1;
2019-09-18 06:14:35 +00:00
PHP
2019-09-18 10:16:31 +00:00
, <<<'PHP'
2018-10-09 10:18:42 +00:00
$string = [];
$string[] = 1;
2019-09-18 06:14:35 +00:00
PHP
2018-10-09 10:18:42 +00:00
)]
);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [Assign::class];
}
/**
* @param Assign $node
2018-10-09 10:18:42 +00:00
*/
public function refactor(Node $node): ?Node
2018-10-09 10:18:42 +00:00
{
// only array with no explicit key assign, e.g. "$value[] = 5";
if (! $node->var instanceof ArrayDimFetch || $node->var->dim !== null) {
return null;
2018-10-09 10:18:42 +00:00
}
$arrayDimFetchNode = $node->var;
2018-10-09 10:18:42 +00:00
/** @var Variable|PropertyFetch|StaticPropertyFetch|Expr $variableNode */
$variableNode = $arrayDimFetchNode->var;
// set default value to property
if (($variableNode instanceof PropertyFetch || $variableNode instanceof StaticPropertyFetch) &&
$this->processProperty($variableNode)
) {
return $node;
2018-10-09 10:18:42 +00:00
}
// fallback to variable, property or static property = '' set
if ($this->processVariable($node, $variableNode)) {
return $node;
2018-10-09 10:18:42 +00:00
}
// there is "$string[] = ...;", which would cause error in PHP 7+
// fallback - if no array init found, retype to (array)
2020-06-29 21:19:37 +00:00
$assign = new Assign($arrayDimFetchNode->var, new ArrayCast($arrayDimFetchNode->var));
2018-10-09 10:18:42 +00:00
$this->addNodeAfterNode(clone $node, $node);
2018-10-09 10:18:42 +00:00
2020-06-29 21:19:37 +00:00
return $assign;
2018-10-09 10:18:42 +00:00
}
/**
* @param Node[] $nodes
2018-11-09 18:32:50 +00:00
* @return Node[]|null
2018-10-09 10:18:42 +00:00
*/
2018-11-09 18:32:50 +00:00
public function beforeTraverse(array $nodes): ?array
2018-10-09 10:18:42 +00:00
{
// collect all known "{anything} = '';" assigns
2019-06-04 20:34:59 +00:00
$this->traverseNodesWithCallable($nodes, function (Node $node): void {
2018-10-09 10:18:42 +00:00
if ($node instanceof PropertyProperty && $node->default && $this->isEmptyStringNode($node->default)) {
$this->emptyStringPropertyNodes[] = $node;
}
});
2018-11-09 18:32:50 +00:00
return $nodes;
2018-10-09 10:18:42 +00:00
}
2018-10-31 15:34:37 +00:00
/**
* @param PropertyFetch|StaticPropertyFetch $propertyNode
*/
private function processProperty(Node $propertyNode): bool
2018-10-09 10:18:42 +00:00
{
2018-10-31 15:34:37 +00:00
foreach ($this->emptyStringPropertyNodes as $emptyStringPropertyNode) {
2019-03-10 23:47:43 +00:00
if ($this->areNamesEqual($emptyStringPropertyNode, $propertyNode)) {
2018-10-31 15:34:37 +00:00
$emptyStringPropertyNode->default = new Array_();
return true;
}
}
return false;
2018-10-09 10:18:42 +00:00
}
/**
2019-02-22 17:25:31 +00:00
* @param Variable|PropertyFetch|StaticPropertyFetch|Expr $expr
2018-10-09 10:18:42 +00:00
*/
2019-02-22 17:25:31 +00:00
private function processVariable(Assign $assign, Expr $expr): bool
2018-10-09 10:18:42 +00:00
{
if ($this->shouldSkipVariable($expr)) {
return true;
2018-10-09 10:18:42 +00:00
}
$variableAssign = $this->betterNodeFinder->findFirstPrevious($assign, function (Node $node) use ($expr): bool {
2018-10-09 10:18:42 +00:00
if (! $node instanceof Assign) {
return false;
}
2019-02-22 17:25:31 +00:00
if (! $this->areNodesEqual($node->var, $expr)) {
2018-10-09 10:18:42 +00:00
return false;
}
// we look for variable assign = string
return $this->isEmptyStringNode($node->expr);
2018-10-09 10:18:42 +00:00
});
if ($variableAssign instanceof Assign) {
$variableAssign->expr = new Array_();
return true;
}
return false;
}
2018-10-31 15:34:37 +00:00
private function isEmptyStringNode(Node $node): bool
2018-10-09 10:18:42 +00:00
{
2018-10-31 15:34:37 +00:00
return $node instanceof String_ && $node->value === '';
2018-10-09 10:18:42 +00:00
}
2019-01-18 02:41:58 +00:00
private function shouldSkipVariable(Expr $expr): bool
2019-01-18 02:41:58 +00:00
{
$staticType = $this->getStaticType($expr);
2019-03-10 23:47:43 +00:00
if ($staticType instanceof ErrorType) {
return false;
}
2019-01-18 02:41:58 +00:00
if ($staticType instanceof UnionType) {
2019-02-27 13:21:11 +00:00
return ! ($staticType->isSuperTypeOf(new ArrayType(new MixedType(), new MixedType()))->yes() &&
$staticType->isSuperTypeOf(new ConstantStringType(''))->yes());
2019-01-18 02:41:58 +00:00
}
2019-02-27 13:21:11 +00:00
return ! $staticType instanceof StringType;
2019-01-18 02:41:58 +00:00
}
2018-10-09 10:18:42 +00:00
}