2019-10-13 05:59:52 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2021-02-20 23:21:19 +00:00
namespace Rector\Arguments\Rector\ClassMethod ;
2018-05-01 17:31:41 +00:00
use PhpParser\BuilderHelpers ;
use PhpParser\Node ;
2018-10-21 19:43:47 +00:00
use PhpParser\Node\Arg ;
2018-05-01 17:31:41 +00:00
use PhpParser\Node\Expr\MethodCall ;
use PhpParser\Node\Expr\StaticCall ;
2018-10-21 19:43:47 +00:00
use PhpParser\Node\Expr\Variable ;
2019-12-04 21:06:09 +00:00
use PhpParser\Node\Name ;
2018-10-21 19:43:47 +00:00
use PhpParser\Node\Param ;
2019-09-06 10:30:58 +00:00
use PhpParser\Node\Stmt\Class_ ;
2018-05-01 17:31:41 +00:00
use PhpParser\Node\Stmt\ClassMethod ;
2021-02-27 02:13:22 +00:00
use PHPStan\Type\ObjectType ;
2021-09-19 11:08:13 +00:00
use PHPStan\Type\Type ;
2021-02-20 23:21:19 +00:00
use Rector\Arguments\NodeAnalyzer\ArgumentAddingScope ;
2021-09-19 09:13:32 +00:00
use Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector ;
2021-02-20 23:21:19 +00:00
use Rector\Arguments\ValueObject\ArgumentAdder ;
2020-07-29 23:39:41 +00:00
use Rector\Core\Contract\Rector\ConfigurableRectorInterface ;
2021-10-25 13:44:53 +00:00
use Rector\Core\Enum\ObjectReference ;
2020-08-25 21:25:00 +00:00
use Rector\Core\Exception\ShouldNotHappenException ;
2020-02-06 21:48:18 +00:00
use Rector\Core\Rector\AbstractRector ;
2021-10-27 19:25:02 +00:00
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind ;
2020-11-16 17:50:38 +00:00
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2021-12-10 00:25:09 +00:00
use RectorPrefix20211210\Webmozart\Assert\Assert ;
2019-09-03 09:11:45 +00:00
/**
2021-03-12 22:20:25 +00:00
* @ see \Rector\Tests\Arguments\Rector\ClassMethod\ArgumentAdderRector\ArgumentAdderRectorTest
2019-09-03 09:11:45 +00:00
*/
2021-05-10 22:23:08 +00:00
final class ArgumentAdderRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\ConfigurableRectorInterface
2018-05-01 17:31:41 +00:00
{
2021-01-24 11:23:50 +00:00
/**
2021-12-10 00:25:09 +00:00
* @ deprecated
2021-01-24 11:23:50 +00:00
* @ var string
*/
public const ADDED_ARGUMENTS = 'added_arguments' ;
2020-07-29 23:39:41 +00:00
/**
2021-01-17 15:43:47 +00:00
* @ var ArgumentAdder []
2020-08-25 21:25:00 +00:00
*/
2021-01-17 15:43:47 +00:00
private $addedArguments = [];
2021-08-27 12:14:08 +00:00
/**
* @ var bool
*/
private $haveArgumentsChanged = \false ;
2018-05-01 17:31:41 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\Arguments\NodeAnalyzer\ArgumentAddingScope
2020-08-25 21:25:00 +00:00
*/
2021-01-17 15:43:47 +00:00
private $argumentAddingScope ;
2021-09-19 09:13:32 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-09-19 09:13:32 +00:00
* @ var \Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector
*/
private $changedArgumentsDetector ;
public function __construct ( \Rector\Arguments\NodeAnalyzer\ArgumentAddingScope $argumentAddingScope , \Rector\Arguments\NodeAnalyzer\ChangedArgumentsDetector $changedArgumentsDetector )
2021-01-17 15:43:47 +00:00
{
$this -> argumentAddingScope = $argumentAddingScope ;
2021-09-19 09:13:32 +00:00
$this -> changedArgumentsDetector = $changedArgumentsDetector ;
2021-01-17 15:43:47 +00:00
}
2021-05-10 22:23:08 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2018-05-01 17:31:41 +00:00
{
2021-05-10 22:23:08 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'This Rector adds new default arguments in calls of defined methods and class types.' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ( <<< 'CODE_SAMPLE'
2018-10-21 19:43:47 +00:00
$someObject = new SomeExampleClass ;
2018-05-04 12:52:17 +00:00
$someObject -> someMethod ();
2021-08-27 12:14:08 +00:00
2018-10-21 19:43:47 +00:00
class MyCustomClass extends SomeExampleClass
2018-05-04 13:01:35 +00:00
{
public function someMethod ()
{
}
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2021-08-27 12:14:08 +00:00
$someObject = new SomeExampleClass ;
$someObject -> someMethod ( true );
2018-10-21 19:43:47 +00:00
class MyCustomClass extends SomeExampleClass
2018-05-04 13:01:35 +00:00
{
public function someMethod ( $value = true )
{
}
}
2020-09-15 08:23:13 +00:00
CODE_SAMPLE
2021-12-06 21:12:03 +00:00
, [ new \Rector\Arguments\ValueObject\ArgumentAdder ( 'SomeExampleClass' , 'someMethod' , 0 , 'someArgument' , \true , new \PHPStan\Type\ObjectType ( 'SomeType' ))])]);
2018-05-01 17:31:41 +00:00
}
2018-08-14 22:12:41 +00:00
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2018-08-14 22:12:41 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2018-05-01 17:31:41 +00:00
{
2021-05-10 22:23:08 +00:00
return [ \PhpParser\Node\Expr\MethodCall :: class , \PhpParser\Node\Expr\StaticCall :: class , \PhpParser\Node\Stmt\ClassMethod :: class ];
2018-05-01 17:31:41 +00:00
}
/**
2021-12-10 09:57:54 +00:00
* @ param \PhpParser\Node $node
2021-08-27 12:14:08 +00:00
* @ return \PhpParser\Node\Expr\MethodCall | \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Stmt\ClassMethod | null
2018-05-01 17:31:41 +00:00
*/
2021-12-10 09:57:54 +00:00
public function refactor ( $node )
2018-05-01 17:31:41 +00:00
{
2021-08-27 12:14:08 +00:00
$this -> haveArgumentsChanged = \false ;
2020-08-25 21:25:00 +00:00
foreach ( $this -> addedArguments as $addedArgument ) {
2021-05-09 20:15:43 +00:00
if ( ! $this -> isObjectTypeMatch ( $node , $addedArgument -> getObjectType ())) {
2018-10-21 19:43:47 +00:00
continue ;
}
2021-05-09 20:15:43 +00:00
if ( ! $this -> isName ( $node -> name , $addedArgument -> getMethod ())) {
2020-08-25 21:25:00 +00:00
continue ;
2018-05-01 17:31:41 +00:00
}
2020-08-25 21:25:00 +00:00
$this -> processPositionWithDefaultValues ( $node , $addedArgument );
2018-05-01 17:31:41 +00:00
}
2021-08-27 12:14:08 +00:00
if ( $this -> haveArgumentsChanged ) {
return $node ;
}
return null ;
2018-05-01 17:31:41 +00:00
}
2020-08-22 20:02:50 +00:00
/**
2021-11-28 17:01:20 +00:00
* @ param mixed [] $configuration
2020-08-22 20:02:50 +00:00
*/
2021-12-10 09:57:54 +00:00
public function configure ( $configuration ) : void
2020-07-29 23:39:41 +00:00
{
2021-11-28 17:01:20 +00:00
$addedArguments = $configuration [ self :: ADDED_ARGUMENTS ] ? ? $configuration ;
2021-12-10 00:25:09 +00:00
\RectorPrefix20211210\Webmozart\Assert\Assert :: allIsAOf ( $addedArguments , \Rector\Arguments\ValueObject\ArgumentAdder :: class );
2020-08-25 21:25:00 +00:00
$this -> addedArguments = $addedArguments ;
2020-07-29 23:39:41 +00:00
}
2019-10-30 09:49:07 +00:00
/**
2021-08-23 00:20:32 +00:00
* @ param \PhpParser\Node\Expr\MethodCall | \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Stmt\ClassMethod $node
2019-10-30 09:49:07 +00:00
*/
2021-06-29 14:24:45 +00:00
private function isObjectTypeMatch ( $node , \PHPStan\Type\ObjectType $objectType ) : bool
2019-10-30 09:49:07 +00:00
{
2021-05-10 22:23:08 +00:00
if ( $node instanceof \PhpParser\Node\Expr\MethodCall ) {
2021-02-27 02:13:22 +00:00
return $this -> isObjectType ( $node -> var , $objectType );
2019-10-30 09:49:07 +00:00
}
2021-05-10 22:23:08 +00:00
if ( $node instanceof \PhpParser\Node\Expr\StaticCall ) {
2021-02-27 02:13:22 +00:00
return $this -> isObjectType ( $node -> class , $objectType );
2019-10-30 09:49:07 +00:00
}
2021-11-06 12:10:48 +00:00
$classLike = $this -> betterNodeFinder -> findParentType ( $node , \PhpParser\Node\Stmt\Class_ :: class );
2021-05-10 22:23:08 +00:00
if ( ! $classLike instanceof \PhpParser\Node\Stmt\Class_ ) {
2021-05-09 20:15:43 +00:00
return \false ;
2019-10-30 09:49:07 +00:00
}
2021-02-27 02:13:22 +00:00
return $this -> isObjectType ( $classLike , $objectType );
2019-10-30 09:49:07 +00:00
}
2018-05-01 17:31:41 +00:00
/**
2021-10-30 14:18:31 +00:00
* @ param \PhpParser\Node\Expr\MethodCall | \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Stmt\ClassMethod $node
2018-05-01 17:31:41 +00:00
*/
2021-05-30 10:12:56 +00:00
private function processPositionWithDefaultValues ( $node , \Rector\Arguments\ValueObject\ArgumentAdder $argumentAdder ) : void
2018-05-01 17:31:41 +00:00
{
2020-09-12 21:19:08 +00:00
if ( $this -> shouldSkipParameter ( $node , $argumentAdder )) {
2020-08-25 21:25:00 +00:00
return ;
}
2020-09-12 21:19:08 +00:00
$defaultValue = $argumentAdder -> getArgumentDefaultValue ();
$argumentType = $argumentAdder -> getArgumentType ();
$position = $argumentAdder -> getPosition ();
2021-05-10 22:23:08 +00:00
if ( $node instanceof \PhpParser\Node\Stmt\ClassMethod ) {
2020-10-09 20:00:45 +00:00
$this -> addClassMethodParam ( $node , $argumentAdder , $defaultValue , $argumentType , $position );
2021-05-10 22:23:08 +00:00
} elseif ( $node instanceof \PhpParser\Node\Expr\StaticCall ) {
2020-10-09 20:00:45 +00:00
$this -> processStaticCall ( $node , $position , $argumentAdder );
2020-08-25 21:25:00 +00:00
} else {
2021-05-10 22:23:08 +00:00
$arg = new \PhpParser\Node\Arg ( \PhpParser\BuilderHelpers :: normalizeValue ( $defaultValue ));
2020-10-09 20:00:45 +00:00
if ( isset ( $node -> args [ $position ])) {
return ;
}
2020-08-25 21:25:00 +00:00
$node -> args [ $position ] = $arg ;
2021-08-27 12:14:08 +00:00
$this -> haveArgumentsChanged = \true ;
2018-05-01 17:31:41 +00:00
}
}
2019-03-08 23:40:33 +00:00
/**
2021-10-30 14:18:31 +00:00
* @ param \PhpParser\Node\Expr\MethodCall | \PhpParser\Node\Expr\StaticCall | \PhpParser\Node\Stmt\ClassMethod $node
2019-03-08 23:40:33 +00:00
*/
2021-06-29 13:37:16 +00:00
private function shouldSkipParameter ( $node , \Rector\Arguments\ValueObject\ArgumentAdder $argumentAdder ) : bool
2019-03-08 23:40:33 +00:00
{
2020-09-12 21:19:08 +00:00
$position = $argumentAdder -> getPosition ();
$argumentName = $argumentAdder -> getArgumentName ();
2021-01-28 19:52:11 +00:00
if ( $argumentName === null ) {
2021-05-09 20:15:43 +00:00
return \true ;
2021-01-28 19:52:11 +00:00
}
2021-05-10 22:23:08 +00:00
if ( $node instanceof \PhpParser\Node\Stmt\ClassMethod ) {
2019-03-08 23:40:33 +00:00
// already added?
2021-05-09 20:15:43 +00:00
if ( ! isset ( $node -> params [ $position ])) {
return \false ;
2020-12-19 15:24:53 +00:00
}
2021-09-19 09:13:32 +00:00
$param = $node -> params [ $position ];
// argument added and name has been changed
if ( ! $this -> isName ( $param , $argumentName )) {
return \true ;
}
// argument added and default has been changed
if ( $this -> changedArgumentsDetector -> isDefaultValueChanged ( $param , $argumentAdder -> getArgumentDefaultValue ())) {
return \true ;
}
// argument added and type has been changed
return $this -> changedArgumentsDetector -> isTypeChanged ( $param , $argumentAdder -> getArgumentType ());
2019-03-08 23:40:33 +00:00
}
2021-08-27 12:14:08 +00:00
if ( isset ( $node -> args [ $position ])) {
return \true ;
2021-02-21 09:32:45 +00:00
}
2021-08-27 12:14:08 +00:00
// is correct scope?
return ! $this -> argumentAddingScope -> isInCorrectScope ( $node , $argumentAdder );
2019-03-08 23:40:33 +00:00
}
2020-07-27 06:56:25 +00:00
/**
* @ param mixed $defaultValue
*/
2021-09-19 11:08:13 +00:00
private function addClassMethodParam ( \PhpParser\Node\Stmt\ClassMethod $classMethod , \Rector\Arguments\ValueObject\ArgumentAdder $argumentAdder , $defaultValue , ? \PHPStan\Type\Type $type , int $position ) : void
2021-05-09 20:15:43 +00:00
{
2020-10-09 20:00:45 +00:00
$argumentName = $argumentAdder -> getArgumentName ();
if ( $argumentName === null ) {
2021-05-10 22:23:08 +00:00
throw new \Rector\Core\Exception\ShouldNotHappenException ();
2020-10-09 20:00:45 +00:00
}
2021-05-10 22:23:08 +00:00
$param = new \PhpParser\Node\Param ( new \PhpParser\Node\Expr\Variable ( $argumentName ), \PhpParser\BuilderHelpers :: normalizeValue ( $defaultValue ));
2021-09-19 11:08:13 +00:00
if ( $type !== null ) {
2021-10-27 19:25:02 +00:00
$typeNode = $this -> staticTypeMapper -> mapPHPStanTypeToPhpParserNode ( $type , \Rector\PHPStanStaticTypeMapper\Enum\TypeKind :: PARAM ());
2021-09-19 11:08:13 +00:00
$param -> type = $typeNode ;
2020-02-01 16:04:38 +00:00
}
$classMethod -> params [ $position ] = $param ;
2021-08-27 12:14:08 +00:00
$this -> haveArgumentsChanged = \true ;
2020-02-01 16:04:38 +00:00
}
2021-05-10 22:23:08 +00:00
private function processStaticCall ( \PhpParser\Node\Expr\StaticCall $staticCall , int $position , \Rector\Arguments\ValueObject\ArgumentAdder $argumentAdder ) : void
2020-02-01 16:04:38 +00:00
{
2020-10-09 20:00:45 +00:00
$argumentName = $argumentAdder -> getArgumentName ();
if ( $argumentName === null ) {
2021-05-10 22:23:08 +00:00
throw new \Rector\Core\Exception\ShouldNotHappenException ();
2020-10-09 20:00:45 +00:00
}
2021-05-10 22:23:08 +00:00
if ( ! $staticCall -> class instanceof \PhpParser\Node\Name ) {
2020-02-01 16:04:38 +00:00
return ;
}
2021-10-25 13:44:53 +00:00
if ( ! $this -> isName ( $staticCall -> class , \Rector\Core\Enum\ObjectReference :: PARENT () -> getValue ())) {
2020-02-01 16:04:38 +00:00
return ;
}
2021-05-10 22:23:08 +00:00
$staticCall -> args [ $position ] = new \PhpParser\Node\Arg ( new \PhpParser\Node\Expr\Variable ( $argumentName ));
2021-08-27 12:14:08 +00:00
$this -> haveArgumentsChanged = \true ;
2020-02-01 16:04:38 +00:00
}
2018-05-01 17:31:41 +00:00
}