rector/rules/Php74/Rector/Property/RestoreDefaultNullToNullableTypePropertyRector.php

131 lines
3.4 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace Rector\Php74\Rector\Property;
use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\NullableType;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PhpParser\NodeTraverser;
use Rector\Core\Rector\AbstractRector;
2020-08-02 13:17:33 +00:00
use Rector\Core\ValueObject\MethodName;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\Php74\Rector\Property\RestoreDefaultNullToNullableTypePropertyRector\RestoreDefaultNullToNullableTypePropertyRectorTest
*/
final class RestoreDefaultNullToNullableTypePropertyRector extends AbstractRector
{
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Add null default to properties with PHP 7.4 property nullable type',
[
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass
{
public ?string $name;
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass
{
public ?string $name = null;
}
CODE_SAMPLE
2021-05-06 18:51:25 +00:00
),
]
);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkip($node)) {
return null;
}
$onlyProperty = $node->props[0];
2021-01-30 21:41:25 +00:00
$onlyProperty->default = $this->nodeFactory->createNull();
return $node;
}
private function shouldSkip(Property $property): bool
{
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::TYPED_PROPERTIES)) {
return true;
}
if (! $property->type instanceof NullableType) {
return true;
}
if (count($property->props) > 1) {
return true;
}
$onlyProperty = $property->props[0];
if ($onlyProperty->default !== null) {
return true;
}
// is variable assigned in constructor
$propertyName = $this->getName($property);
2020-07-06 20:07:11 +00:00
return $this->isPropertyInitiatedInConstuctor($property, $propertyName);
}
private function isPropertyInitiatedInConstuctor(Property $property, string $propertyName): bool
{
2020-07-19 18:52:42 +00:00
$classLike = $property->getAttribute(AttributeKey::CLASS_NODE);
if (! $classLike instanceof Class_) {
return false;
}
2020-08-02 13:17:33 +00:00
$constructClassMethod = $classLike->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return false;
}
$isPropertyInitiated = false;
$this->traverseNodesWithCallable((array) $constructClassMethod->stmts, function (Node $node) use (
$propertyName,
&$isPropertyInitiated
): ?int {
if (! $node instanceof Assign) {
return null;
}
if (! $this->nodeNameResolver->isLocalPropertyFetchNamed($node->var, $propertyName)) {
return null;
}
$isPropertyInitiated = true;
return NodeTraverser::STOP_TRAVERSAL;
});
return $isPropertyInitiated;
}
}