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\ClassLike ;
use PhpParser\Node\Stmt\ClassMethod ;
use PhpParser\Node\Stmt\Interface_ ;
use PHPStan\Type\ObjectType ;
use Rector\BetterPhpDocParser\PhpDocParser\PhpDocFromTypeDeclarationDecorator ;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface ;
use Rector\Core\Rector\AbstractRector ;
use Rector\Core\ValueObject\PhpVersionFeature ;
use Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer ;
use Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory ;
use Rector\VersionBonding\Contract\MinPhpVersionInterface ;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2021-12-27 15:34:11 +00:00
/**
* @ see \Rector\Tests\Transform\Rector\ClassMethod\ReturnTypeWillChangeRector\ReturnTypeWillChangeRectorTest
*/
2022-06-06 17:12:56 +00:00
final class ReturnTypeWillChangeRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface , \Rector\VersionBonding\Contract\MinPhpVersionInterface
2021-12-27 15:34:11 +00:00
{
/**
* @ var array < string , string [] >
*/
private $classMethodsOfClass = [];
/**
* @ readonly
* @ var \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer
*/
private $phpAttributeAnalyzer ;
/**
* @ 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-06-06 17:12:56 +00:00
public function __construct ( \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer $phpAttributeAnalyzer , \Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory $phpAttributeGroupFactory )
2021-12-27 15:34:11 +00:00
{
$this -> phpAttributeAnalyzer = $phpAttributeAnalyzer ;
$this -> phpAttributeGroupFactory = $phpAttributeGroupFactory ;
}
2022-06-06 17:12:56 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2021-12-27 15:34:11 +00:00
{
2022-06-06 17:12:56 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Add #[\\ReturnTypeWillChange] attribute to configured instanceof class with methods' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\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
, [ 'ArrayAccess' => [ 'offsetGet' ]])]);
}
/**
* @ return array < class - string < Node >>
*/
public function getNodeTypes () : array
{
2022-06-06 17:12:56 +00:00
return [ \PhpParser\Node\Stmt\ClassMethod :: class ];
2021-12-27 15:34:11 +00:00
}
/**
* @ param ClassMethod $node
*/
2022-06-06 17:12:56 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2021-12-27 15:34:11 +00:00
{
if ( $this -> phpAttributeAnalyzer -> hasPhpAttribute ( $node , 'ReturnTypeWillChange' )) {
return null ;
}
if ( $node -> returnType !== null ) {
return null ;
}
2022-06-06 17:12:56 +00:00
$classLike = $this -> betterNodeFinder -> findParentByTypes ( $node , [ \PhpParser\Node\Stmt\Class_ :: class , \PhpParser\Node\Stmt\Interface_ :: class ]);
if ( ! $classLike instanceof \PhpParser\Node\Stmt\ClassLike ) {
2021-12-27 15:34:11 +00:00
return null ;
}
/** @var array<string, string[]> $classMethodsOfClass */
$classMethodsOfClass = \array_merge_recursive ( $this -> resolveDefaultConfig (), $this -> classMethodsOfClass );
$className = ( string ) $this -> nodeNameResolver -> getName ( $classLike );
2022-06-06 17:12:56 +00:00
$objectType = new \PHPStan\Type\ObjectType ( $className );
2021-12-27 15:34:11 +00:00
$methodName = $this -> nodeNameResolver -> getName ( $node );
$hasChanged = \false ;
foreach ( $classMethodsOfClass as $class => $methods ) {
2022-06-06 17:12:56 +00:00
$configuredClassObjectType = new \PHPStan\Type\ObjectType ( $class );
2021-12-27 15:34:11 +00:00
if ( ! $configuredClassObjectType -> isSuperTypeOf ( $objectType ) -> yes ()) {
continue ;
}
if ( ! \in_array ( $methodName , $methods , \true )) {
continue ;
}
2022-06-06 17:12:56 +00:00
$attributeGroup = $this -> phpAttributeGroupFactory -> createFromClass ( \Rector\BetterPhpDocParser\PhpDocParser\PhpDocFromTypeDeclarationDecorator :: RETURN_TYPE_WILL_CHANGE_ATTRIBUTE );
2021-12-27 15:34:11 +00:00
$node -> attrGroups [] = $attributeGroup ;
$hasChanged = \true ;
break ;
}
if ( ! $hasChanged ) {
return null ;
}
return $node ;
}
/**
* @ param mixed [] $configuration
*/
public function configure ( array $configuration ) : void
{
$this -> classMethodsOfClass = $configuration ;
}
public function provideMinPhpVersion () : int
{
2022-06-06 17:12:56 +00:00
return \Rector\Core\ValueObject\PhpVersionFeature :: RETURN_TYPE_WILL_CHANGE_ATTRIBUTE ;
2021-12-27 15:34:11 +00:00
}
/**
* @ return array < string , string [] >
*/
private function resolveDefaultConfig () : array
{
$configuration = [];
2022-06-06 17:12:56 +00:00
foreach ( \Rector\BetterPhpDocParser\PhpDocParser\PhpDocFromTypeDeclarationDecorator :: ADD_RETURN_TYPE_WILL_CHANGE as $classWithMethods ) {
2021-12-27 15:34:11 +00:00
foreach ( $classWithMethods as $class => $methods ) {
$configuration [ $class ] = \array_merge ( $configuration [ $class ] ? ? [], $methods );
}
}
return $configuration ;
}
}