2021-11-26 11:19:23 +00:00
< ? php
2021-11-26 17:20:22 +00:00
declare ( strict_types = 1 );
2022-06-06 17:12:56 +00:00
namespace Rector\Transform\Rector\Class_ ;
2021-11-26 11:19:23 +00:00
2022-06-06 17:12:56 +00:00
use PhpParser\Node ;
use PhpParser\Node\Name\FullyQualified ;
use PhpParser\Node\Stmt\Class_ ;
use PHPStan\Reflection\ReflectionProvider ;
use Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface ;
use Rector\Core\Rector\AbstractRector ;
use Rector\Core\ValueObject\MethodName ;
use Rector\Core\ValueObject\PhpVersionFeature ;
use Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer ;
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 ;
2022-06-07 06:40:10 +00:00
use RectorPrefix20220607\Webmozart\Assert\Assert ;
2021-11-26 11:19:23 +00:00
/**
* @ changelog https :// wiki . php . net / rfc / deprecate_dynamic_properties
*
* @ see \Rector\Tests\Transform\Rector\Class_\AddAllowDynamicPropertiesAttributeRector\AddAllowDynamicPropertiesAttributeRectorTest
*/
2022-06-06 17:12:56 +00:00
final class AddAllowDynamicPropertiesAttributeRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\AllowEmptyConfigurableRectorInterface , \Rector\VersionBonding\Contract\MinPhpVersionInterface
2021-11-26 11:19:23 +00:00
{
/**
* @ var string
*/
private const ATTRIBUTE = 'AllowDynamicProperties' ;
2021-12-10 01:09:37 +00:00
/**
* @ var array < array - key , string >
*/
private $transformOnNamespaces = [];
2021-11-26 11:19:23 +00:00
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-11-26 11:19:23 +00:00
* @ var \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer
*/
private $familyRelationsAnalyzer ;
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-11-26 11:19:23 +00:00
* @ var \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer
*/
private $phpAttributeAnalyzer ;
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2022-05-28 00:27:30 +00:00
* @ var \Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory
2021-11-26 11:19:23 +00:00
*/
private $phpAttributeGroupFactory ;
/**
2021-12-04 12:47:17 +00:00
* @ readonly
2021-11-26 11:19:23 +00:00
* @ var \PHPStan\Reflection\ReflectionProvider
*/
private $reflectionProvider ;
2022-06-06 17:12:56 +00:00
public function __construct ( \Rector\FamilyTree\Reflection\FamilyRelationsAnalyzer $familyRelationsAnalyzer , \Rector\Php80\NodeAnalyzer\PhpAttributeAnalyzer $phpAttributeAnalyzer , \Rector\PhpAttribute\NodeFactory\PhpAttributeGroupFactory $phpAttributeGroupFactory , \PHPStan\Reflection\ReflectionProvider $reflectionProvider )
2021-11-26 11:19:23 +00:00
{
$this -> familyRelationsAnalyzer = $familyRelationsAnalyzer ;
$this -> phpAttributeAnalyzer = $phpAttributeAnalyzer ;
$this -> phpAttributeGroupFactory = $phpAttributeGroupFactory ;
$this -> reflectionProvider = $reflectionProvider ;
}
2022-06-06 17:12:56 +00:00
public function getRuleDefinition () : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
2021-11-26 11:19:23 +00:00
{
2022-06-06 17:12:56 +00:00
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition ( 'Add the `AllowDynamicProperties` attribute to all classes' , [ new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample ( <<< 'CODE_SAMPLE'
2021-12-10 01:09:37 +00:00
namespace Example\Domain ;
2021-11-26 11:19:23 +00:00
class SomeObject {
public string $someProperty = 'hello world' ;
}
CODE_SAMPLE
, <<< 'CODE_SAMPLE'
2021-12-10 01:09:37 +00:00
namespace Example\Domain ;
2021-11-26 11:19:23 +00:00
#[AllowDynamicProperties]
class SomeObject {
public string $someProperty = 'hello world' ;
}
CODE_SAMPLE
2021-12-10 07:56:16 +00:00
, [ 'Example\\*' ])]);
2021-11-26 11:19:23 +00:00
}
2021-11-26 17:20:22 +00:00
/**
* @ return array < class - string < Node >>
*/
2021-11-26 11:19:23 +00:00
public function getNodeTypes () : array
{
2022-06-06 17:12:56 +00:00
return [ \PhpParser\Node\Stmt\Class_ :: class ];
2021-11-26 11:19:23 +00:00
}
2021-12-10 10:22:23 +00:00
public function configure ( array $configuration ) : void
2021-12-10 01:09:37 +00:00
{
2021-12-10 07:56:16 +00:00
$transformOnNamespaces = $configuration ;
2022-06-07 06:40:10 +00:00
\RectorPrefix20220607\Webmozart\Assert\Assert :: allString ( $transformOnNamespaces );
2021-12-10 01:09:37 +00:00
$this -> transformOnNamespaces = $transformOnNamespaces ;
}
2021-11-26 11:19:23 +00:00
/**
2021-12-10 10:22:23 +00:00
* @ param Class_ $node
2021-11-26 11:19:23 +00:00
*/
2022-06-06 17:12:56 +00:00
public function refactor ( \PhpParser\Node $node ) : ? \PhpParser\Node
2021-11-26 11:19:23 +00:00
{
2021-11-26 17:20:22 +00:00
if ( $this -> shouldSkip ( $node )) {
2021-11-26 11:19:23 +00:00
return null ;
}
return $this -> addAllowDynamicPropertiesAttribute ( $node );
}
public function provideMinPhpVersion () : int
{
2022-06-06 17:12:56 +00:00
return \Rector\Core\ValueObject\PhpVersionFeature :: DEPRECATE_DYNAMIC_PROPERTIES ;
2021-11-26 11:19:23 +00:00
}
2022-06-06 17:12:56 +00:00
private function isDescendantOfStdclass ( \PhpParser\Node\Stmt\Class_ $class ) : bool
2021-11-26 11:19:23 +00:00
{
2022-06-06 17:12:56 +00:00
if ( ! $class -> extends instanceof \PhpParser\Node\Name\FullyQualified ) {
2021-11-26 11:19:23 +00:00
return \false ;
}
2021-11-26 17:20:22 +00:00
$ancestorClassNames = $this -> familyRelationsAnalyzer -> getClassLikeAncestorNames ( $class );
return \in_array ( 'stdClass' , $ancestorClassNames , \true );
2021-11-26 11:19:23 +00:00
}
2022-06-06 17:12:56 +00:00
private function hasNeededAttributeAlready ( \PhpParser\Node\Stmt\Class_ $class ) : bool
2021-11-26 11:19:23 +00:00
{
$nodeHasAttribute = $this -> phpAttributeAnalyzer -> hasPhpAttribute ( $class , self :: ATTRIBUTE );
if ( $nodeHasAttribute ) {
return \true ;
}
2022-06-06 17:12:56 +00:00
if ( ! $class -> extends instanceof \PhpParser\Node\Name\FullyQualified ) {
2021-11-26 11:19:23 +00:00
return \false ;
}
return $this -> phpAttributeAnalyzer -> hasInheritedPhpAttribute ( $class , self :: ATTRIBUTE );
}
2022-06-06 17:12:56 +00:00
private function addAllowDynamicPropertiesAttribute ( \PhpParser\Node\Stmt\Class_ $class ) : \PhpParser\Node\Stmt\Class_
2021-11-26 11:19:23 +00:00
{
$attributeGroup = $this -> phpAttributeGroupFactory -> createFromClass ( self :: ATTRIBUTE );
$class -> attrGroups [] = $attributeGroup ;
return $class ;
}
2022-06-06 17:12:56 +00:00
private function shouldSkip ( \PhpParser\Node\Stmt\Class_ $class ) : bool
2021-11-26 17:20:22 +00:00
{
2021-12-10 07:56:16 +00:00
if ( $this -> transformOnNamespaces !== []) {
2021-12-10 01:09:37 +00:00
$className = ( string ) $this -> nodeNameResolver -> getName ( $class );
foreach ( $this -> transformOnNamespaces as $transformOnNamespace ) {
if ( ! $this -> nodeNameResolver -> isStringName ( $className , $transformOnNamespace )) {
return \true ;
}
}
}
2021-11-26 17:20:22 +00:00
if ( $this -> isDescendantOfStdclass ( $class )) {
return \true ;
}
if ( $this -> hasNeededAttributeAlready ( $class )) {
return \true ;
}
return $this -> hasMagicSetMethod ( $class );
}
2022-06-06 17:12:56 +00:00
private function hasMagicSetMethod ( \PhpParser\Node\Stmt\Class_ $class ) : bool
2021-11-26 17:20:22 +00:00
{
$className = ( string ) $this -> getName ( $class );
$classReflection = $this -> reflectionProvider -> getClass ( $className );
2022-06-06 17:12:56 +00:00
return $classReflection -> hasMethod ( \Rector\Core\ValueObject\MethodName :: __SET );
2021-11-26 17:20:22 +00:00
}
2021-11-26 11:19:23 +00:00
}