Skip to content

Commit

Permalink
[PHP 8.0] Add property support to ConstantListClassToEnumRector (#2422)
Browse files Browse the repository at this point in the history
* add property enum support

* fixup! add property enum support
  • Loading branch information
TomasVotruba committed Jun 3, 2022
1 parent 7722a57 commit 0df2351
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class EnumTypeProperty
{
/**
* @var Gear::* $gear
*/
private $gear;
}

?>
-----
<?php

namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;

use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;

final class EnumTypeProperty
{
private \Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear $gear;
}

?>
34 changes: 29 additions & 5 deletions rules/Php80/NodeAnalyzer/EnumParamAnalyzer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Php80\ValueObject\ClassNameAndTagValueNode;

/**
* Detects enum-like params, e.g.
Expand All @@ -25,8 +27,10 @@ public function __construct(
) {
}

public function matchParameterClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string
{
public function matchParameterClassName(
ParameterReflection $parameterReflection,
PhpDocInfo $phpDocInfo
): ?ClassNameAndTagValueNode {
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return null;
Expand All @@ -41,17 +45,37 @@ public function matchParameterClassName(ParameterReflection $parameterReflection
return null;
}

return $className;
return new ClassNameAndTagValueNode($className, $paramTagValueNode);
}

public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?string
public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?ClassNameAndTagValueNode
{
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
if (! $returnTagValueNode instanceof ReturnTagValueNode) {
return null;
}

return $this->resolveClassFromConstType($returnTagValueNode->type);
$className = $this->resolveClassFromConstType($returnTagValueNode->type);
if (! is_string($className)) {
return null;
}

return new ClassNameAndTagValueNode($className, $returnTagValueNode);
}

public function matchPropertyClassName(PhpDocInfo $phpDocInfo): ?ClassNameAndTagValueNode
{
$varTagValueNode = $phpDocInfo->getVarTagValueNode();
if (! $varTagValueNode instanceof VarTagValueNode) {
return null;
}

$className = $this->resolveClassFromConstType($varTagValueNode->type);
if (! is_string($className)) {
return null;
}

return new ClassNameAndTagValueNode($className, $varTagValueNode);
}

private function resolveClassFromConstType(TypeNode $typeNode): ?string
Expand Down
56 changes: 39 additions & 17 deletions rules/Php80/Rector/Class_/ConstantListClassToEnumRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PhpParser\Node\Stmt\Property;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
Expand All @@ -19,6 +18,7 @@
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Php80\NodeAnalyzer\EnumConstListClassDetector;
use Rector\Php80\NodeAnalyzer\EnumParamAnalyzer;
use Rector\Php80\ValueObject\ClassNameAndTagValueNode;
use Rector\Php81\NodeFactory\EnumFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
Expand Down Expand Up @@ -68,11 +68,11 @@ enum Direction
*/
public function getNodeTypes(): array
{
return [Class_::class, ClassMethod::class];
return [Class_::class, ClassMethod::class, Property::class];
}

/**
* @param Class_|ClassMethod $node
* @param Class_|ClassMethod|Property $node
*/
public function refactor(Node $node): ?Node
{
Expand All @@ -84,7 +84,11 @@ public function refactor(Node $node): ?Node
return $this->enumFactory->createFromClass($node);
}

return $this->refactorClassMethod($node);
if ($node instanceof ClassMethod) {
return $this->refactorClassMethod($node);
}

return $this->refactorProperty($node);
}

private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod
Expand Down Expand Up @@ -137,8 +141,11 @@ private function refactorParams(

$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
foreach ($parametersAcceptor->getParameters() as $parameterReflection) {
$enumLikeClass = $this->enumParamAnalyzer->matchParameterClassName($parameterReflection, $phpDocInfo);
if ($enumLikeClass === null) {
$classNameAndTagValueNode = $this->enumParamAnalyzer->matchParameterClassName(
$parameterReflection,
$phpDocInfo
);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
continue;
}

Expand All @@ -148,30 +155,45 @@ private function refactorParams(
}

// change and remove
$param->type = new FullyQualified($enumLikeClass);
$param->type = new FullyQualified($classNameAndTagValueNode->getEnumClass());
$hasNodeChanged = true;

/** @var ParamTagValueNode $paramTagValueNode */
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());
}

return $hasNodeChanged;
}

private function refactorReturn(PhpDocInfo $phpDocInfo, ClassMethod $classMethod): bool
{
$returnType = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo);
if ($returnType === null) {
$classNameAndTagValueNode = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
return false;
}

$classMethod->returnType = new FullyQualified($returnType);
$classMethod->returnType = new FullyQualified($classNameAndTagValueNode->getEnumClass());

/** @var ReturnTagValueNode $returnTagValueNode */
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $returnTagValueNode);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());

return true;
}

private function refactorProperty(Property $property): ?Property
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($property);
if (! $phpDocInfo instanceof PhpDocInfo) {
return null;
}

$classNameAndTagValueNode = $this->enumParamAnalyzer->matchPropertyClassName($phpDocInfo);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
return null;
}

$property->type = new FullyQualified($classNameAndTagValueNode->getEnumClass());

$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());

return $property;
}
}
28 changes: 28 additions & 0 deletions rules/Php80/ValueObject/ClassNameAndTagValueNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Php80\ValueObject;

use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;

final class ClassNameAndTagValueNode
{
public function __construct(
private readonly string $enumClass,
private readonly ParamTagValueNode|ReturnTagValueNode|VarTagValueNode $tagValueNode
) {
}

public function getEnumClass(): string
{
return $this->enumClass;
}

public function getTagValueNode(): ParamTagValueNode|ReturnTagValueNode|VarTagValueNode
{
return $this->tagValueNode;
}
}

0 comments on commit 0df2351

Please sign in to comment.