2020-12-08 12:03:00 +00:00
< ? php
2021-05-09 20:15:43 +00:00
declare ( strict_types = 1 );
2022-06-06 17:12:56 +00:00
namespace Rector\DowngradePhp72\Rector\ClassMethod ;
2020-12-08 12:03:00 +00:00
2022-06-06 17:12:56 +00:00
use PhpParser\Node ;
use PhpParser\Node\Param ;
use PhpParser\Node\Stmt\ClassLike ;
use PhpParser\Node\Stmt\ClassMethod ;
use PHPStan\Reflection\ClassReflection ;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface ;
use Rector\Core\Rector\AbstractRector ;
use Rector\Core\Reflection\ReflectionResolver ;
use Rector\DowngradePhp72\NodeAnalyzer\BuiltInMethodAnalyzer ;
use Rector\DowngradePhp72\NodeAnalyzer\OverrideFromAnonymousClassMethodAnalyzer ;
use Rector\DowngradePhp72\NodeAnalyzer\SealedClassAnalyzer ;
use Rector\DowngradePhp72\PhpDoc\NativeParamToPhpDocDecorator ;
use Rector\TypeDeclaration\NodeAnalyzer\AutowiredClassMethodOrPropertyAnalyzer ;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition ;
2022-06-07 06:40:10 +00:00
use RectorPrefix20220607\Webmozart\Assert\Assert ;
2020-12-08 12:03:00 +00:00
/**
2021-04-10 18:47:17 +00:00
* @ changelog https :// www . php . net / manual / en / migration72 . new - features . php #migration72.new-features.param-type-widening
2022-05-12 19:17:07 +00:00
* @ changelog https :// 3 v4l . org / fOgSE
2020-12-08 12:03:00 +00:00
*
2021-07-04 18:27:51 +00:00
* @ see \Rector\Tests\DowngradePhp72\Rector\ClassMethod\DowngradeParameterTypeWideningRector\DowngradeParameterTypeWideningRectorTest
2020-12-08 12:03:00 +00:00
*/
2022-06-06 17:12:56 +00:00
final class DowngradeParameterTypeWideningRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface
2020-12-08 12:03:00 +00:00
{
2021-12-10 09:57:54 +00:00
/**
* @ var array < string , string [] >
*/
private $unsafeTypesToMethods = [];
2021-04-21 22:02:38 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-05-10 23:39:21 +00:00
* @ var \Rector\DowngradePhp72\PhpDoc\NativeParamToPhpDocDecorator
2021-04-21 22:02:38 +00:00
*/
2021-04-23 09:03:45 +00:00
private $nativeParamToPhpDocDecorator ;
2021-06-08 13:00:02 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-11-07 04:15:35 +00:00
* @ var \Rector\Core\Reflection\ReflectionResolver
2021-06-08 13:00:02 +00:00
*/
2021-11-07 04:15:35 +00:00
private $reflectionResolver ;
2021-07-06 13:09:53 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-07-25 07:47:04 +00:00
* @ var \Rector\TypeDeclaration\NodeAnalyzer\AutowiredClassMethodOrPropertyAnalyzer
2021-07-06 13:09:53 +00:00
*/
2021-07-25 11:00:04 +00:00
private $autowiredClassMethodOrPropertyAnalyzer ;
2021-08-26 08:02:58 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-08-26 08:02:58 +00:00
* @ var \Rector\DowngradePhp72\NodeAnalyzer\BuiltInMethodAnalyzer
*/
private $builtInMethodAnalyzer ;
2021-08-27 09:14:46 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-08-27 09:14:46 +00:00
* @ var \Rector\DowngradePhp72\NodeAnalyzer\OverrideFromAnonymousClassMethodAnalyzer
*/
private $overrideFromAnonymousClassMethodAnalyzer ;
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-08-27 09:14:46 +00:00
* @ var \Rector\DowngradePhp72\NodeAnalyzer\SealedClassAnalyzer
*/
private $sealedClassAnalyzer ;
2022-06-06 17:12:56 +00:00
public function __construct ( \Rector\DowngradePhp72\PhpDoc\NativeParamToPhpDocDecorator $nativeParamToPhpDocDecorator , \Rector\Core\Reflection\ReflectionResolver $reflectionResolver , \Rector\TypeDeclaration\NodeAnalyzer\AutowiredClassMethodOrPropertyAnalyzer $autowiredClassMethodOrPropertyAnalyzer , \Rector\DowngradePhp72\NodeAnalyzer\BuiltInMethodAnalyzer $builtInMethodAnalyzer , \Rector\DowngradePhp72\NodeAnalyzer\OverrideFromAnonymousClassMethodAnalyzer $overrideFromAnonymousClassMethodAnalyzer , \Rector\DowngradePhp72\NodeAnalyzer\SealedClassAnalyzer $sealedClassAnalyzer )
2021-05-09 20:15:43 +00:00
{
2021-04-23 09:03:45 +00:00
$this -> nativeParamToPhpDocDecorator = $nativeParamToPhpDocDecorator ;
2021-11-07 04:15:35 +00:00
$this -> reflectionResolver = $reflectionResolver ;
2021-07-25 11:00:04 +00:00
$this -> autowiredClassMethodOrPropertyAnalyzer = $autowiredClassMethodOrPropertyAnalyzer ;
2021-08-26 08:02:58 +00:00
$this -> builtInMethodAnalyzer = $builtInMethodAnalyzer ;
2021-08-27 09:14:46 +00:00
$this -> overrideFromAnonymousClassMethodAnalyzer = $overrideFromAnonymousClassMethodAnalyzer ;
$this -> sealedClassAnalyzer = $sealedClassAnalyzer ;
2020-12-08 12:03:00 +00:00
}
2022-06-06 17:12:56 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2020-12-08 12:03:00 +00:00
{
2022-06-06 17:12:56 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Change param type to match the lowest type in whole family tree' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ( <<< 'CODE_SAMPLE'
2021-04-23 17:54:30 +00:00
interface SomeInterface
2020-12-08 12:03:00 +00:00
{
public function test ( array $input );
}
2021-04-23 17:54:30 +00:00
final class SomeClass implements SomeInterface
2020-12-08 12:03:00 +00:00
{
2021-04-23 17:54:30 +00:00
public function test ( $input )
{
}
2020-12-08 12:03:00 +00:00
}
CODE_SAMPLE
2021-05-09 20:15:43 +00:00
, <<< 'CODE_SAMPLE'
2021-04-23 17:54:30 +00:00
interface SomeInterface
2020-12-08 12:03:00 +00:00
{
2021-04-23 17:54:30 +00:00
/**
* @ param mixed [] $input
*/
public function test ( $input );
2020-12-08 12:03:00 +00:00
}
2021-04-23 17:54:30 +00:00
final class SomeClass implements SomeInterface
2020-12-08 12:03:00 +00:00
{
2021-04-23 17:54:30 +00:00
public function test ( $input )
{
}
2020-12-08 12:03:00 +00:00
}
CODE_SAMPLE
2021-12-10 18:12:28 +00:00
, [ 'ContainerInterface' => [ 'set' , 'get' , 'has' , 'initialized' ], 'SomeContainerInterface' => [ 'set' , 'has' ]])]);
2020-12-08 12:03:00 +00:00
}
/**
2021-02-27 00:06:15 +00:00
* @ return array < class - string < Node >>
2020-12-08 12:03:00 +00:00
*/
2021-05-09 20:15:43 +00:00
public function getNodeTypes () : array
2020-12-08 12:03:00 +00:00
{
2022-06-06 17:12:56 +00:00
return [ \PhpParser\Node\Stmt\ClassMethod :: class ];
2020-12-08 12:03:00 +00:00
}
/**
2021-12-10 10:22:23 +00:00
* @ param ClassMethod $node
2020-12-08 12:03:00 +00:00
*/
2022-06-06 17:12:56 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2020-12-08 12:03:00 +00:00
{
2022-06-06 17:12:56 +00:00
$classLike = $this -> betterNodeFinder -> findParentType ( $node , \PhpParser\Node\Stmt\ClassLike :: class );
if ( ! $classLike instanceof \PhpParser\Node\Stmt\ClassLike ) {
2021-04-23 09:03:45 +00:00
return null ;
}
2021-09-02 06:26:12 +00:00
$ancestorOverridableAnonymousClass = $this -> overrideFromAnonymousClassMethodAnalyzer -> matchAncestorClassReflectionOverrideable ( $classLike , $node );
2022-06-06 17:12:56 +00:00
if ( $ancestorOverridableAnonymousClass instanceof \PHPStan\Reflection\ClassReflection ) {
2021-09-02 06:26:12 +00:00
return $this -> processRemoveParamTypeFromMethod ( $ancestorOverridableAnonymousClass , $node );
2021-08-27 09:14:46 +00:00
}
2021-11-07 04:15:35 +00:00
$classReflection = $this -> reflectionResolver -> resolveClassAndAnonymousClass ( $classLike );
2021-09-02 06:26:12 +00:00
return $this -> processRemoveParamTypeFromMethod ( $classReflection , $node );
2020-12-08 12:03:00 +00:00
}
2021-03-17 10:12:39 +00:00
/**
2021-11-28 17:01:20 +00:00
* @ param mixed [] $configuration
2021-03-17 10:12:39 +00:00
*/
2021-12-10 10:22:23 +00:00
public function configure ( array $configuration ) : void
2020-12-08 12:03:00 +00:00
{
2021-12-10 18:12:28 +00:00
$unsafeTypesToMethods = $configuration ;
2021-12-10 09:57:54 +00:00
foreach ( $unsafeTypesToMethods as $key => $value ) {
2022-06-07 06:40:10 +00:00
\RectorPrefix20220607\Webmozart\Assert\Assert :: string ( $key );
\RectorPrefix20220607\Webmozart\Assert\Assert :: allString ( $value );
2021-12-10 09:57:54 +00:00
}
$this -> unsafeTypesToMethods = $unsafeTypesToMethods ;
2021-04-23 17:54:30 +00:00
}
2022-06-06 17:12:56 +00:00
private function shouldSkip ( \PHPStan\Reflection\ClassReflection $classReflection , \PhpParser\Node\Stmt\ClassMethod $classMethod ) : bool
2021-08-27 09:14:46 +00:00
{
2021-11-05 10:25:23 +00:00
if ( $classMethod -> params === []) {
return \true ;
}
if ( $classMethod -> isPrivate ()) {
return \true ;
}
if ( $classMethod -> isMagic ()) {
return \true ;
}
2021-08-27 09:14:46 +00:00
if ( $this -> sealedClassAnalyzer -> isSealedClass ( $classReflection )) {
return \true ;
}
2021-11-05 10:25:23 +00:00
if ( $this -> autowiredClassMethodOrPropertyAnalyzer -> detect ( $classMethod )) {
2021-08-27 09:14:46 +00:00
return \true ;
}
2021-11-05 10:25:23 +00:00
if ( $this -> hasParamAlreadyNonTyped ( $classMethod )) {
2021-08-27 09:14:46 +00:00
return \true ;
}
2021-11-05 10:25:23 +00:00
return $this -> isSafeType ( $classReflection , $classMethod );
2021-08-27 09:14:46 +00:00
}
2022-06-06 17:12:56 +00:00
private function processRemoveParamTypeFromMethod ( \PHPStan\Reflection\ClassReflection $classReflection , \PhpParser\Node\Stmt\ClassMethod $classMethod ) : ? \PhpParser\Node\Stmt\ClassMethod
2021-08-27 09:14:46 +00:00
{
2021-09-02 06:26:12 +00:00
if ( $this -> shouldSkip ( $classReflection , $classMethod )) {
return null ;
}
if ( $this -> builtInMethodAnalyzer -> isImplementsBuiltInInterface ( $classReflection , $classMethod )) {
return null ;
}
2021-08-27 09:14:46 +00:00
// Downgrade every scalar parameter, just to be sure
foreach ( \array_keys ( $classMethod -> params ) as $paramPosition ) {
$this -> removeParamTypeFromMethod ( $classMethod , $paramPosition );
}
return $classMethod ;
}
2022-06-06 17:12:56 +00:00
private function removeParamTypeFromMethod ( \PhpParser\Node\Stmt\ClassMethod $classMethod , int $paramPosition ) : void
2021-04-23 17:54:30 +00:00
{
2021-07-05 07:47:59 +00:00
$param = $classMethod -> params [ $paramPosition ] ? ? null ;
2022-06-06 17:12:56 +00:00
if ( ! $param instanceof \PhpParser\Node\Param ) {
2021-07-05 07:47:59 +00:00
return ;
}
2020-12-08 12:03:00 +00:00
// Add the current type in the PHPDoc
2021-04-23 09:03:45 +00:00
$this -> nativeParamToPhpDocDecorator -> decorate ( $classMethod , $param );
2020-12-08 12:03:00 +00:00
$param -> type = null ;
}
2022-06-06 17:12:56 +00:00
private function hasParamAlreadyNonTyped ( \PhpParser\Node\Stmt\ClassMethod $classMethod ) : bool
2021-04-23 09:03:45 +00:00
{
2021-07-06 13:09:53 +00:00
foreach ( $classMethod -> params as $param ) {
if ( $param -> type !== null ) {
return \false ;
}
}
return \true ;
2021-07-04 18:27:51 +00:00
}
2022-06-06 17:12:56 +00:00
private function isSafeType ( \PHPStan\Reflection\ClassReflection $classReflection , \PhpParser\Node\Stmt\ClassMethod $classMethod ) : bool
2021-07-05 23:37:59 +00:00
{
2021-12-10 18:12:28 +00:00
if ( $this -> unsafeTypesToMethods === []) {
return \false ;
}
2021-11-05 10:25:23 +00:00
$classReflectionName = $classReflection -> getName ();
2021-12-10 09:57:54 +00:00
foreach ( $this -> unsafeTypesToMethods as $unsafeType => $unsafeMethods ) {
if ( ! $this -> isNames ( $classMethod , $unsafeMethods )) {
continue ;
}
if ( $classReflection -> isSubclassOf ( $unsafeType )) {
return \false ;
}
// skip self too
if ( $classReflectionName === $unsafeType ) {
return \false ;
}
}
2021-12-10 10:22:23 +00:00
return \true ;
2021-07-05 23:37:59 +00:00
}
2020-12-08 12:03:00 +00:00
}