2020-04-24 17:03:14 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2020-04-24 17:03:14 +00:00
namespace Rector\CodeQuality\Rector\Foreach_ ;
use PhpParser\Node ;
2021-07-09 23:57:04 +00:00
use PhpParser\Node\Expr ;
2020-07-07 19:19:54 +00:00
use PhpParser\Node\Expr\Array_ ;
2020-12-21 02:12:42 +00:00
use PhpParser\Node\Expr\ArrayItem ;
2021-05-25 11:50:23 +00:00
use PhpParser\Node\Expr\FuncCall ;
2020-07-07 19:19:54 +00:00
use PhpParser\Node\Expr\Variable ;
2020-04-24 17:03:14 +00:00
use PhpParser\Node\Stmt\Foreach_ ;
2020-12-02 13:09:35 +00:00
use PHPStan\Type\ObjectType ;
2021-05-25 11:50:23 +00:00
use Rector\Core\NodeAnalyzer\CompactFuncCallAnalyzer ;
2020-04-24 17:03:14 +00:00
use Rector\Core\Rector\AbstractRector ;
2020-11-16 17:50:38 +00:00
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2020-04-24 17:03:14 +00:00
/**
2021-03-12 22:20:25 +00:00
* @ see \Rector\Tests\CodeQuality\Rector\Foreach_\UnusedForeachValueToArrayKeysRector\UnusedForeachValueToArrayKeysRectorTest
2020-04-24 17:03:14 +00:00
*/
2021-05-10 22:23:08 +00:00
final class UnusedForeachValueToArrayKeysRector extends \Rector\Core\Rector\AbstractRector
2020-04-24 17:03:14 +00:00
{
2021-05-25 11:50:23 +00:00
/**
* @ var \Rector\Core\NodeAnalyzer\CompactFuncCallAnalyzer
*/
private $compactFuncCallAnalyzer ;
public function __construct ( \Rector\Core\NodeAnalyzer\CompactFuncCallAnalyzer $compactFuncCallAnalyzer )
{
$this -> compactFuncCallAnalyzer = $compactFuncCallAnalyzer ;
}
2021-05-10 22:23:08 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2020-04-24 17:03:14 +00:00
{
2021-05-10 22:23:08 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Change foreach with unused $value but only $key, to array_keys()' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2020-04-24 17:03:14 +00:00
class SomeClass
{
public function run ()
{
$items = [];
foreach ( $values as $key => $value ) {
$items [ $key ] = null ;
}
}
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2020-04-24 17:03:14 +00:00
class SomeClass
{
public function run ()
{
$items = [];
foreach ( array_keys ( $values ) as $key ) {
$items [ $key ] = null ;
}
}
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
)]);
2020-04-24 17:03:14 +00:00
}
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2020-04-24 17:03:14 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2020-04-24 17:03:14 +00:00
{
2021-05-10 22:23:08 +00:00
return [ \PhpParser\Node\Stmt\Foreach_ :: class ];
2020-04-24 17:03:14 +00:00
}
/**
* @ param Foreach_ $node
*/
2021-05-10 22:23:08 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2020-04-24 17:03:14 +00:00
{
if ( $node -> keyVar === null ) {
return null ;
}
2020-07-07 19:19:54 +00:00
// special case of nested array items
2021-05-10 22:23:08 +00:00
if ( $node -> valueVar instanceof \PhpParser\Node\Expr\Array_ ) {
2020-07-07 19:19:54 +00:00
$node -> valueVar = $this -> refactorArrayForeachValue ( $node -> valueVar , $node );
2020-10-29 08:59:39 +00:00
if ( $node -> valueVar -> items !== []) {
2020-07-07 19:19:54 +00:00
return null ;
}
2021-05-10 22:23:08 +00:00
} elseif ( $node -> valueVar instanceof \PhpParser\Node\Expr\Variable ) {
2020-07-07 19:19:54 +00:00
if ( $this -> isVariableUsedInForeach ( $node -> valueVar , $node )) {
return null ;
}
} else {
2020-04-24 17:03:14 +00:00
return null ;
}
2021-07-09 23:57:04 +00:00
if ( ! $this -> isArrayType ( $node -> expr )) {
2020-12-02 13:09:35 +00:00
return null ;
}
2020-07-07 19:19:54 +00:00
$this -> removeForeachValueAndUseArrayKeys ( $node );
return $node ;
}
2021-05-10 22:23:08 +00:00
private function refactorArrayForeachValue ( \PhpParser\Node\Expr\Array_ $array , \PhpParser\Node\Stmt\Foreach_ $foreach ) : \PhpParser\Node\Expr\Array_
2020-07-07 19:19:54 +00:00
{
foreach ( $array -> items as $key => $arrayItem ) {
2021-05-10 22:23:08 +00:00
if ( ! $arrayItem instanceof \PhpParser\Node\Expr\ArrayItem ) {
2020-12-21 02:12:42 +00:00
continue ;
}
2020-07-07 19:19:54 +00:00
$value = $arrayItem -> value ;
2021-05-10 22:23:08 +00:00
if ( ! $value instanceof \PhpParser\Node\Expr\Variable ) {
2020-07-07 19:19:54 +00:00
return $array ;
}
if ( $this -> isVariableUsedInForeach ( $value , $foreach )) {
continue ;
}
unset ( $array -> items [ $key ]);
}
return $array ;
}
2021-05-10 22:23:08 +00:00
private function isVariableUsedInForeach ( \PhpParser\Node\Expr\Variable $variable , \PhpParser\Node\Stmt\Foreach_ $foreach ) : bool
2020-07-07 19:19:54 +00:00
{
2021-05-10 22:23:08 +00:00
return ( bool ) $this -> betterNodeFinder -> findFirst ( $foreach -> stmts , function ( \PhpParser\Node $node ) use ( $variable ) : bool {
2021-05-25 11:50:23 +00:00
$isVariableUsed = $this -> nodeComparator -> areNodesEqual ( $node , $variable );
if ( $isVariableUsed ) {
return \true ;
}
if ( ! $node instanceof \PhpParser\Node\Expr\FuncCall ) {
return \false ;
}
return $this -> compactFuncCallAnalyzer -> isInCompact ( $node , $variable );
2020-07-07 19:19:54 +00:00
});
2020-04-24 17:03:14 +00:00
}
2021-05-10 22:23:08 +00:00
private function removeForeachValueAndUseArrayKeys ( \PhpParser\Node\Stmt\Foreach_ $foreach ) : void
2020-08-05 20:45:36 +00:00
{
// remove key value
$foreach -> valueVar = $foreach -> keyVar ;
$foreach -> keyVar = null ;
2021-01-30 21:41:25 +00:00
$foreach -> expr = $this -> nodeFactory -> createFuncCall ( 'array_keys' , [ $foreach -> expr ]);
2020-08-05 20:45:36 +00:00
}
2021-07-09 23:57:04 +00:00
private function isArrayType ( \PhpParser\Node\Expr $expr ) : bool
{
$exprType = $this -> getStaticType ( $expr );
if ( $exprType instanceof \PHPStan\Type\ObjectType ) {
return \false ;
}
return $exprType -> isArray () -> yes ();
}
2020-04-24 17:03:14 +00:00
}