2021-12-27 15:34:11 +00:00
< ? php
declare ( strict_types = 1 );
2022-06-06 17:12:56 +00:00
namespace Rector\Transform\Rector\ClassMethod ;
2021-12-27 15:34:11 +00:00
2022-06-06 17:12:56 +00:00
use PhpParser\Node ;
use PhpParser\Node\Stmt\Class_ ;
use PhpParser\Node\Stmt\Interface_ ;
2024-01-02 02:40:38 +00:00
use Rector\Contract\Rector\ConfigurableRectorInterface ;
2022-06-06 17:12:56 +00:00
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer ;
2022-08-11 15:03:59 +00:00
use Rector\Php81\Enum\AttributeName ;
2022-06-06 17:12:56 +00:00
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory ;
2024-01-02 02:40:38 +00:00
use Rector\Rector\AbstractRector ;
use Rector\Reflection\ReflectionResolver ;
2022-08-11 15:03:59 +00:00
use Rector\Transform\ValueObject\ClassMethodReference ;
2024-01-02 02:40:38 +00:00
use Rector\ValueObject\PhpVersionFeature ;
2022-06-06 17:12:56 +00:00
use Rector\VersionBonding\Contract\MinPhpVersionInterface ;
2022-06-07 09:18:30 +00:00
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2024-03-01 20:02:28 +00:00
use RectorPrefix202403\Webmozart\Assert\Assert ;
2021-12-27 15:34:11 +00:00
/**
* @ see \Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\ReturnTypeWillChangeRectorTest
*/
2023-07-19 11:08:33 +00:00
final class ReturnTypeWillChangeRector extends AbstractRector implements MinPhpVersionInterface , ConfigurableRectorInterface
2021-12-27 15:34:11 +00:00
{
/**
2023-06-11 23:01:39 +00:00
* @ readonly
2021-12-27 15:34:11 +00:00
* @ var \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer
*/
private $phpAttributeAnalyzer ;
/**
2023-06-11 23:01:39 +00:00
* @ readonly
2022-05-28 00:27:30 +00:00
* @ var \Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory
2021-12-27 15:34:11 +00:00
*/
private $phpAttributeGroupFactory ;
2022-08-11 15:03:59 +00:00
/**
2023-06-11 23:01:39 +00:00
* @ readonly
2024-01-02 02:40:38 +00:00
* @ var \Rector\Reflection\ReflectionResolver
2022-08-11 15:03:59 +00:00
*/
private $reflectionResolver ;
2023-06-08 22:00:17 +00:00
/**
* @ var ClassMethodReference []
*/
private $returnTypeChangedClassMethodReferences = [];
2022-08-11 15:03:59 +00:00
public function __construct ( PhpAttributeAnalyzer $phpAttributeAnalyzer , PhpAttributeGroupFactory $phpAttributeGroupFactory , ReflectionResolver $reflectionResolver )
2021-12-27 15:34:11 +00:00
{
$this -> phpAttributeAnalyzer = $phpAttributeAnalyzer ;
$this -> phpAttributeGroupFactory = $phpAttributeGroupFactory ;
2022-08-11 15:03:59 +00:00
$this -> reflectionResolver = $reflectionResolver ;
2023-06-12 11:13:09 +00:00
$this -> returnTypeChangedClassMethodReferences = [ new ClassMethodReference ( 'ArrayAccess' , 'getIterator' ), new ClassMethodReference ( 'ArrayAccess' , 'offsetGet' )];
2021-12-27 15:34:11 +00:00
}
2022-06-07 08:22:29 +00:00
public function getRuleDefinition () : RuleDefinition
2021-12-27 15:34:11 +00:00
{
2022-06-07 08:22:29 +00:00
return new RuleDefinition ( 'Add #[\\ReturnTypeWillChange] attribute to configured instanceof class with methods' , [ new ConfiguredCodeSample ( <<< 'CODE_SAMPLE'
2021-12-27 15:34:11 +00:00
class SomeClass implements ArrayAccess
{
public function offsetGet ( $offset )
{
}
}
CODE_SAMPLE
, <<< 'CODE_SAMPLE'
class SomeClass implements ArrayAccess
{
#[\ReturnTypeWillChange]
public function offsetGet ( $offset )
{
}
}
CODE_SAMPLE
2022-08-11 15:03:59 +00:00
, [ new ClassMethodReference ( 'ArrayAccess' , 'offsetGet' )])]);
2021-12-27 15:34:11 +00:00
}
/**
* @ return array < class - string < Node >>
*/
public function getNodeTypes () : array
{
2023-06-12 11:13:09 +00:00
return [ Class_ :: class , Interface_ :: class ];
2021-12-27 15:34:11 +00:00
}
/**
2023-06-12 11:13:09 +00:00
* @ param Class_ | Interface_ $node
2021-12-27 15:34:11 +00:00
*/
2022-06-07 08:22:29 +00:00
public function refactor ( Node $node ) : ? Node
2021-12-27 15:34:11 +00:00
{
$hasChanged = \false ;
2023-06-12 11:13:09 +00:00
$classReflection = $this -> reflectionResolver -> resolveClassAndAnonymousClass ( $node );
foreach ( $node -> getMethods () as $classMethod ) {
if ( $this -> phpAttributeAnalyzer -> hasPhpAttribute ( $classMethod , AttributeName :: RETURN_TYPE_WILL_CHANGE )) {
2021-12-27 15:34:11 +00:00
continue ;
}
2023-06-12 11:13:09 +00:00
// the return type is known, no need to add attribute
if ( $classMethod -> returnType !== null ) {
2021-12-27 15:34:11 +00:00
continue ;
}
2023-06-12 11:13:09 +00:00
foreach ( $this -> returnTypeChangedClassMethodReferences as $returnTypeChangedClassMethodReference ) {
if ( ! $classReflection -> isSubclassOf ( $returnTypeChangedClassMethodReference -> getClass ())) {
continue ;
}
if ( ! $this -> isName ( $classMethod , $returnTypeChangedClassMethodReference -> getMethod ())) {
continue ;
}
$classMethod -> attrGroups [] = $this -> phpAttributeGroupFactory -> createFromClass ( AttributeName :: RETURN_TYPE_WILL_CHANGE );
$hasChanged = \true ;
break ;
}
2021-12-27 15:34:11 +00:00
}
2023-06-12 11:13:09 +00:00
if ( $hasChanged ) {
return $node ;
2021-12-27 15:34:11 +00:00
}
2023-06-12 11:13:09 +00:00
return null ;
2021-12-27 15:34:11 +00:00
}
/**
* @ param mixed [] $configuration
*/
public function configure ( array $configuration ) : void
{
2022-08-11 15:03:59 +00:00
Assert :: allIsInstanceOf ( $configuration , ClassMethodReference :: class );
$this -> returnTypeChangedClassMethodReferences = \array_merge ( $this -> returnTypeChangedClassMethodReferences , $configuration );
2021-12-27 15:34:11 +00:00
}
public function provideMinPhpVersion () : int
{
2022-06-07 08:22:29 +00:00
return PhpVersionFeature :: RETURN_TYPE_WILL_CHANGE_ATTRIBUTE ;
2021-12-27 15:34:11 +00:00
}
}