mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-31 16:30:51 +00:00
[TypeDeclaration] Add AddPropertyTypeDeclaration rule (#1317)
This commit is contained in:
parent
5a0947d9a2
commit
99a337285a
|
@ -1,4 +1,4 @@
|
|||
# 498 Rules Overview
|
||||
# 501 Rules Overview
|
||||
|
||||
<br>
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
|||
|
||||
- [DowngradePhp56](#downgradephp56) (4)
|
||||
|
||||
- [DowngradePhp70](#downgradephp70) (12)
|
||||
- [DowngradePhp70](#downgradephp70) (13)
|
||||
|
||||
- [DowngradePhp71](#downgradephp71) (10)
|
||||
|
||||
|
@ -98,9 +98,9 @@
|
|||
|
||||
- [Strict](#strict) (5)
|
||||
|
||||
- [Transform](#transform) (35)
|
||||
- [Transform](#transform) (36)
|
||||
|
||||
- [TypeDeclaration](#typedeclaration) (21)
|
||||
- [TypeDeclaration](#typedeclaration) (22)
|
||||
|
||||
- [Visibility](#visibility) (2)
|
||||
|
||||
|
@ -4300,6 +4300,19 @@ Remove anonymous class
|
|||
|
||||
<br>
|
||||
|
||||
### DowngradeClosureCallRector
|
||||
|
||||
Replace `Closure::call()` by `Closure::bindTo()`
|
||||
|
||||
- class: [`Rector\DowngradePhp70\Rector\MethodCall\DowngradeClosureCallRector`](../rules/DowngradePhp70/Rector/MethodCall/DowngradeClosureCallRector.php)
|
||||
|
||||
```diff
|
||||
-$closure->call($newObj, ...$args);
|
||||
+call_user_func($closure->bindTo($newObj, $newObj), ...$args);
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### DowngradeDefineArrayConstantRector
|
||||
|
||||
Change array contant definition via define to const
|
||||
|
@ -10167,6 +10180,21 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
## Transform
|
||||
|
||||
### AddAllowDynamicPropertiesAttributeRector
|
||||
|
||||
Add the `AllowDynamicProperties` attribute to all classes
|
||||
|
||||
- class: [`Rector\Transform\Rector\Class_\AddAllowDynamicPropertiesAttributeRector`](../rules/Transform/Rector/Class_/AddAllowDynamicPropertiesAttributeRector.php)
|
||||
|
||||
```diff
|
||||
+#[AllowDynamicProperties]
|
||||
class SomeObject {
|
||||
public string $someProperty = 'hello world';
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### AddInterfaceByParentRector
|
||||
|
||||
Add interface by parent
|
||||
|
@ -11805,6 +11833,44 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
<br>
|
||||
|
||||
### AddPropertyTypeDeclarationRector
|
||||
|
||||
Add type to property by added rules, mostly public/property by parent type
|
||||
|
||||
:wrench: **configure it!**
|
||||
|
||||
- class: [`Rector\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector`](../rules/TypeDeclaration/Rector/Property/AddPropertyTypeDeclarationRector.php)
|
||||
|
||||
```php
|
||||
use PHPStan\Type\StringType;
|
||||
use Rector\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\ValueObject\AddPropertyTypeDeclaration;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(AddPropertyTypeDeclarationRector::class)
|
||||
->call(
|
||||
'configure',
|
||||
[[ValueObjectInliner::inline(new AddPropertyTypeDeclaration('ParentClass', 'name', new StringType()))]]
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```diff
|
||||
class SomeClass extends ParentClass
|
||||
{
|
||||
- public $name;
|
||||
+ public string $name;
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### AddReturnTypeDeclarationRector
|
||||
|
||||
Changes defined return typehint of method and class.
|
||||
|
|
|
@ -215,4 +215,9 @@ final class AttributeKey
|
|||
* @var string
|
||||
*/
|
||||
public const STATEMENT_DEPTH = 'statementDepth';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const HAS_NEW_INHERITED_TYPE = 'has_new_inherited_type';
|
||||
}
|
||||
|
|
|
@ -560,3 +560,6 @@ parameters:
|
|||
-
|
||||
path: rules/Php80/NodeManipulator/TokenManipulator.php
|
||||
message: '#Property set "\$node\-\>expr" is overridden#'
|
||||
|
||||
# @todo fix in symplify
|
||||
- '#Parameter \#3 \$configuration of class Symplify\\RuleDocGenerator\\ValueObject\\CodeSample\\ConfiguredCodeSample constructor expects array<string, mixed\>, array<int, Rector\\TypeDeclaration\\ValueObject\\AddPropertyTypeDeclaration\> given#'
|
||||
|
|
|
@ -10,26 +10,23 @@ use Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRecto
|
|||
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\ValueObject\AddParamTypeDeclaration;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(AddParamTypeDeclarationRector::class)
|
||||
->call('configure', [[
|
||||
AddParamTypeDeclarationRector::PARAMETER_TYPEHINTS => ValueObjectInliner::inline([
|
||||
new AddParamTypeDeclaration(
|
||||
ParentInterfaceWithChangeTypeInterface::class,
|
||||
'process',
|
||||
0,
|
||||
new StringType()
|
||||
),
|
||||
new AddParamTypeDeclaration(ParserInterface::class, 'parse', 0, new StringType()),
|
||||
new AddParamTypeDeclaration(
|
||||
ClassMetadataFactory::class,
|
||||
'setEntityManager',
|
||||
0,
|
||||
new ObjectType('Doctrine\ORM\EntityManagerInterface')
|
||||
),
|
||||
]),
|
||||
]]);
|
||||
->configure([
|
||||
new AddParamTypeDeclaration(
|
||||
ParentInterfaceWithChangeTypeInterface::class,
|
||||
'process',
|
||||
0,
|
||||
new StringType()
|
||||
),
|
||||
new AddParamTypeDeclaration(ParserInterface::class, 'parse', 0, new StringType()),
|
||||
new AddParamTypeDeclaration(
|
||||
ClassMetadataFactory::class,
|
||||
'setEntityManager',
|
||||
0,
|
||||
new ObjectType('Doctrine\ORM\EntityManagerInterface')
|
||||
),
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class AddPropertyTypeDeclarationRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configured_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source\ParentClassWithProperty;
|
||||
|
||||
final class SkipAlreadyAddedType extends ParentClassWithProperty
|
||||
{
|
||||
public int $name;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source\ParentClassWithProperty;
|
||||
|
||||
final class SkipDifferentPropertyName extends ParentClassWithProperty
|
||||
{
|
||||
public $surname;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source\ParentClassWithProperty;
|
||||
|
||||
final class SomeClass extends ParentClassWithProperty
|
||||
{
|
||||
public $name;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Fixture;
|
||||
|
||||
use Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source\ParentClassWithProperty;
|
||||
|
||||
final class SomeClass extends ParentClassWithProperty
|
||||
{
|
||||
public string $name;
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source;
|
||||
|
||||
abstract class ParentClassWithProperty
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use PHPStan\Type\StringType;
|
||||
use Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\Source\ParentClassWithProperty;
|
||||
use Rector\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector;
|
||||
use Rector\TypeDeclaration\ValueObject\AddPropertyTypeDeclaration;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(AddPropertyTypeDeclarationRector::class)
|
||||
->configure([new AddPropertyTypeDeclaration(ParentClassWithProperty::class, 'name', new StringType())]);
|
||||
};
|
|
@ -109,11 +109,11 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* @param array<string, AddParamTypeDeclaration[]> $configuration
|
||||
* @param array<string, AddParamTypeDeclaration[]>|AddParamTypeDeclaration[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$parameterTypehints = $configuration[self::PARAMETER_TYPEHINTS] ?? [];
|
||||
$parameterTypehints = $configuration[self::PARAMETER_TYPEHINTS] ?? $configuration ?: [];
|
||||
Assert::allIsInstanceOf($parameterTypehints, AddParamTypeDeclaration::class);
|
||||
|
||||
$this->parameterTypehints = $parameterTypehints;
|
||||
|
|
|
@ -18,7 +18,6 @@ use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
|||
use Rector\StaticTypeMapper\ValueObject\Type\NonExistingObjectType;
|
||||
use Rector\TypeDeclaration\NodeTypeAnalyzer\TraitTypeAnalyzer;
|
||||
use Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||
use Rector\TypeDeclaration\ValueObject\NewType;
|
||||
use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard;
|
||||
use Rector\VendorLocker\VendorLockResolver;
|
||||
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
|
||||
|
@ -208,6 +207,6 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
// already set → skip
|
||||
return ! $param->type->getAttribute(NewType::HAS_NEW_INHERITED_TYPE, false);
|
||||
return ! $param->type->getAttribute(AttributeKey::HAS_NEW_INHERITED_TYPE, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\Rector\Property;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Type\StringType;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
|
||||
use Rector\TypeDeclaration\ValueObject\AddPropertyTypeDeclaration;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @see \Rector\Tests\TypeDeclaration\Rector\Property\AddPropertyTypeDeclarationRector\AddPropertyTypeDeclarationRectorTest
|
||||
*/
|
||||
final class AddPropertyTypeDeclarationRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var AddPropertyTypeDeclaration[]
|
||||
*/
|
||||
private array $addPropertyTypeDeclarations = [];
|
||||
|
||||
public function getRuleDefinition(): RuleDefinition
|
||||
{
|
||||
$configuration = [new AddPropertyTypeDeclaration('ParentClass', 'name', new StringType())];
|
||||
|
||||
return new RuleDefinition('Add type to property by added rules, mostly public/property by parent type', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass extends ParentClass
|
||||
{
|
||||
public $name;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass extends ParentClass
|
||||
{
|
||||
public string $name;
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
$configuration
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<class-string<Node>>
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Property::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
// this would be nice
|
||||
// public function refactorWithScope(Node $node, Scope $scope): ?Node
|
||||
{
|
||||
// type is already known
|
||||
if ($node->type !== null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$classReflection = $scope->getClassReflection();
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->addPropertyTypeDeclarations as $addPropertyTypeDeclaration) {
|
||||
if (! $classReflection->isSubclassOf($addPropertyTypeDeclaration->getClass())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isName($node, $addPropertyTypeDeclaration->getPropertyName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$typeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode(
|
||||
$addPropertyTypeDeclaration->getType(),
|
||||
TypeKind::PROPERTY()
|
||||
);
|
||||
if ($typeNode === null) {
|
||||
// invalid configuration
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$node->type = $typeNode;
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AddPropertyTypeDeclaration[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
Assert::allIsAOf($configuration, AddPropertyTypeDeclaration::class);
|
||||
$this->addPropertyTypeDeclarations = $configuration;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\ValueObject;
|
||||
|
||||
use PHPStan\Type\Type;
|
||||
|
||||
final class AddPropertyTypeDeclaration
|
||||
{
|
||||
public function __construct(
|
||||
private string $class,
|
||||
private string $propertyName,
|
||||
private Type $type
|
||||
) {
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getPropertyName(): string
|
||||
{
|
||||
return $this->propertyName;
|
||||
}
|
||||
|
||||
public function getType(): Type
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\TypeDeclaration\ValueObject;
|
||||
|
||||
final class NewType
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const HAS_NEW_INHERITED_TYPE = 'has_new_inherited_type';
|
||||
}
|
Loading…
Reference in New Issue
Block a user