2019-10-13 05:59:52 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2019-09-22 18:57:03 +00:00
namespace Rector\Php73\Rector\FuncCall ;
2018-10-10 15:51:14 +00:00
use PhpParser\Node ;
use PhpParser\Node\Expr\FuncCall ;
use PhpParser\Node\Name ;
2021-03-03 21:28:27 +00:00
use PhpParser\Node\Stmt\Expression ;
2021-02-28 07:47:48 +00:00
use PHPStan\Reflection\ReflectionProvider ;
2020-02-06 21:48:18 +00:00
use Rector\Core\Rector\AbstractRector ;
use Rector\Core\ValueObject\PhpVersionFeature ;
2021-03-03 21:28:27 +00:00
use Rector\NodeTypeResolver\Node\AttributeKey ;
2021-07-21 09:35:57 +00:00
use Rector\VersionBonding\Contract\MinPhpVersionInterface ;
2020-11-16 17:50:38 +00:00
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2018-10-10 15:51:14 +00:00
/**
2021-04-10 18:47:17 +00:00
* @ changelog https :// tomasvotruba . com / blog / 2018 / 08 / 16 / whats - new - in - php - 73 - in - 30 - seconds - in - diffs / #2-first-and-last-array-key
2018-10-10 15:51:14 +00:00
*
* This needs to removed 1 floor above , because only nodes in arrays can be removed why traversing ,
* see https :// github . com / nikic / PHP - Parser / issues / 389
2021-02-28 07:47:48 +00:00
*
2021-03-12 22:20:25 +00:00
* @ see \Rector\Tests\Php73\Rector\FuncCall\ArrayKeyFirstLastRector\ArrayKeyFirstLastRectorTest
2018-10-10 15:51:14 +00:00
*/
2021-07-21 09:35:57 +00:00
final class ArrayKeyFirstLastRector extends \Rector\Core\Rector\AbstractRector implements \Rector\VersionBonding\Contract\MinPhpVersionInterface
2018-10-10 15:51:14 +00:00
{
2019-07-02 16:04:54 +00:00
/**
* @ var string
*/
private const ARRAY_KEY_FIRST = 'array_key_first' ;
/**
* @ var string
*/
private const ARRAY_KEY_LAST = 'array_key_last' ;
2018-10-10 15:51:14 +00:00
/**
2020-09-01 17:56:30 +00:00
* @ var array < string , string >
2018-10-10 15:51:14 +00:00
*/
2021-05-09 20:15:43 +00:00
private const PREVIOUS_TO_NEW_FUNCTIONS = [ 'reset' => self :: ARRAY_KEY_FIRST , 'end' => self :: ARRAY_KEY_LAST ];
2021-02-28 07:47:48 +00:00
/**
2021-08-22 23:17:31 +00:00
* @ var \RectorPrefix20210822\PHPStan\Reflection\ReflectionProvider
2021-02-28 07:47:48 +00:00
*/
private $reflectionProvider ;
2021-05-10 22:23:08 +00:00
public function __construct ( \PHPStan\Reflection\ReflectionProvider $reflectionProvider )
2021-02-28 07:47:48 +00:00
{
$this -> reflectionProvider = $reflectionProvider ;
}
2021-05-10 22:23:08 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2018-10-10 15:51:14 +00:00
{
2021-05-10 22:23:08 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Make use of array_key_first() and array_key_last()' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2018-10-10 15:51:14 +00:00
reset ( $items );
$firstKey = key ( $items );
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2018-10-10 15:51:14 +00:00
$firstKey = array_key_first ( $items );
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-10 22:23:08 +00:00
), new \Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample ( <<< 'CODE_SAMPLE'
2018-10-10 15:51:14 +00:00
end ( $items );
$lastKey = key ( $items );
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2018-10-10 15:51:14 +00:00
$lastKey = array_key_last ( $items );
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
)]);
2018-10-10 15:51:14 +00:00
}
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2018-10-10 15:51:14 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2018-10-10 15:51:14 +00:00
{
2021-05-10 22:23:08 +00:00
return [ \PhpParser\Node\Expr\FuncCall :: class ];
2018-10-10 15:51:14 +00:00
}
/**
2021-07-05 22:50:18 +00:00
* @ param FuncCall $node
2018-10-10 15:51:14 +00:00
*/
2021-07-05 22:50:18 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2018-10-10 15:51:14 +00:00
{
2019-07-02 16:04:54 +00:00
if ( $this -> shouldSkip ( $node )) {
2018-10-22 15:53:21 +00:00
return null ;
2018-10-10 15:51:14 +00:00
}
2019-02-23 09:11:01 +00:00
$nextExpression = $this -> getNextExpression ( $node );
2021-05-10 22:23:08 +00:00
if ( ! $nextExpression instanceof \PhpParser\Node ) {
2018-10-22 15:53:21 +00:00
return null ;
2018-10-10 15:51:14 +00:00
}
2019-02-23 09:11:01 +00:00
$resetOrEndFuncCall = $node ;
2021-05-10 22:23:08 +00:00
$keyFuncCall = $this -> betterNodeFinder -> findFirst ( $nextExpression , function ( \PhpParser\Node $node ) use ( $resetOrEndFuncCall ) : bool {
if ( ! $node instanceof \PhpParser\Node\Expr\FuncCall ) {
2021-05-09 20:15:43 +00:00
return \false ;
2019-02-23 09:11:01 +00:00
}
2021-05-09 20:15:43 +00:00
if ( ! $this -> isName ( $node , 'key' )) {
return \false ;
2019-02-23 09:11:01 +00:00
}
2021-02-19 12:01:23 +00:00
return $this -> nodeComparator -> areNodesEqual ( $resetOrEndFuncCall -> args [ 0 ], $node -> args [ 0 ]);
2019-02-23 09:11:01 +00:00
});
2021-05-10 22:23:08 +00:00
if ( ! $keyFuncCall instanceof \PhpParser\Node\Expr\FuncCall ) {
2018-10-22 15:53:21 +00:00
return null ;
2018-10-10 15:51:14 +00:00
}
2020-02-18 22:09:25 +00:00
$newName = self :: PREVIOUS_TO_NEW_FUNCTIONS [ $this -> getName ( $node )];
2021-05-10 22:23:08 +00:00
$keyFuncCall -> name = new \PhpParser\Node\Name ( $newName );
2019-02-23 09:11:01 +00:00
$this -> removeNode ( $node );
2018-10-22 15:53:21 +00:00
return $node ;
2018-10-10 15:51:14 +00:00
}
2021-07-21 09:35:57 +00:00
public function provideMinPhpVersion () : int
{
return \Rector\Core\ValueObject\PhpVersionFeature :: ARRAY_KEY_FIRST_LAST ;
}
2021-07-05 22:50:18 +00:00
private function shouldSkip ( \PhpParser\Node\Expr\FuncCall $funcCall ) : bool
2019-07-02 16:04:54 +00:00
{
2021-05-09 20:15:43 +00:00
if ( ! $this -> isNames ( $funcCall , [ 'reset' , 'end' ])) {
return \true ;
2019-12-26 13:31:04 +00:00
}
2021-05-10 22:23:08 +00:00
if ( ! $this -> reflectionProvider -> hasFunction ( new \PhpParser\Node\Name ( self :: ARRAY_KEY_FIRST ), null )) {
2021-05-09 20:15:43 +00:00
return \true ;
2021-02-28 07:47:48 +00:00
}
2021-05-10 22:23:08 +00:00
return ! $this -> reflectionProvider -> hasFunction ( new \PhpParser\Node\Name ( self :: ARRAY_KEY_LAST ), null );
2019-07-02 16:04:54 +00:00
}
2021-07-05 22:50:18 +00:00
private function getNextExpression ( \PhpParser\Node\Expr\FuncCall $funcCall ) : ? \PhpParser\Node
2021-03-03 21:28:27 +00:00
{
2021-05-10 22:23:08 +00:00
$currentExpression = $funcCall -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: CURRENT_STATEMENT );
if ( ! $currentExpression instanceof \PhpParser\Node\Stmt\Expression ) {
2021-03-03 21:28:27 +00:00
return null ;
}
2021-05-10 22:23:08 +00:00
return $currentExpression -> getAttribute ( \Rector\NodeTypeResolver\Node\AttributeKey :: NEXT_NODE );
2021-03-03 21:28:27 +00:00
}
2018-10-10 15:51:14 +00:00
}