diff --git a/composer.json b/composer.json index b94cc1adb57..be30f7dbea2 100644 --- a/composer.json +++ b/composer.json @@ -58,6 +58,7 @@ "phpunit/phpunit": "^9.5", "rector/phpstan-rules": "^0.3.3", "rector/rector-generator": "^0.3", + "spatie/enum": "^3.9", "symplify/coding-standard": "^9.4.28", "symplify/easy-ci": "^9.4.28", "symplify/easy-coding-standard": "^9.4.28", diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc new file mode 100644 index 00000000000..7e083403f76 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc @@ -0,0 +1,31 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php new file mode 100644 index 00000000000..e62309966cf --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/SpatieEnumClassToEnumRectorTest.php @@ -0,0 +1,33 @@ +doTestFileInfo($fileInfo); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php new file mode 100644 index 00000000000..298e7c9f5cf --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule.php @@ -0,0 +1,11 @@ +services(); + $services->set(SpatieEnumClassToEnumRector::class); +}; diff --git a/rules/Php81/NodeFactory/EnumFactory.php b/rules/Php81/NodeFactory/EnumFactory.php index e4d9384f9c5..50101c1af1c 100644 --- a/rules/Php81/NodeFactory/EnumFactory.php +++ b/rules/Php81/NodeFactory/EnumFactory.php @@ -4,17 +4,24 @@ namespace Rector\Php81\NodeFactory; +use Nette\Utils\Strings; +use PhpParser\BuilderFactory; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\Enum_; use PhpParser\Node\Stmt\EnumCase; +use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; +use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\NodeNameResolver\NodeNameResolver; use Rector\NodeTypeResolver\Node\AttributeKey; final class EnumFactory { public function __construct( - private NodeNameResolver $nodeNameResolver + private NodeNameResolver $nodeNameResolver, + private PhpDocInfoFactory $phpDocInfoFactory, + private BuilderFactory $builderFactory ) { } @@ -25,21 +32,47 @@ public function createFromClass(Class_ $class): Enum_ // constant to cases foreach ($class->getConstants() as $classConst) { - $enum->stmts[] = $this->createEnumCase($classConst); + $enum->stmts[] = $this->createEnumCaseFromConst($classConst); } return $enum; } - private function createEnumCase(ClassConst $classConst): EnumCase + public function createFromSpatieClass(Class_ $class): Enum_ + { + $shortClassName = $this->nodeNameResolver->getShortName($class); + $enum = new Enum_($shortClassName); + + // constant to cases + $classDocInfo = $this->phpDocInfoFactory->createFromNode($class); + $docBlockMethods = $classDocInfo?->getTagsByName('@method'); + if ($docBlockMethods !== null) { + foreach ($docBlockMethods as $docBlockMethod) { + $enum->stmts[] = $this->createEnumCaseFromDocComment($docBlockMethod); + } + } + + return $enum; + } + + private function createEnumCaseFromConst(ClassConst $classConst): EnumCase { $constConst = $classConst->consts[0]; $enumCase = new EnumCase($constConst->name, $constConst->value); - // mirrow comments + // mirror comments $enumCase->setAttribute(AttributeKey::PHP_DOC_INFO, $classConst->getAttribute(AttributeKey::PHP_DOC_INFO)); $enumCase->setAttribute(AttributeKey::COMMENTS, $classConst->getAttribute(AttributeKey::COMMENTS)); return $enumCase; } + + private function createEnumCaseFromDocComment(PhpDocTagNode $docTagNode): EnumCase + { + /** + * @var MethodTagValueNode $nodeValue + */ + $nodeValue = $docTagNode->value; + return new EnumCase($nodeValue->methodName, $this->builderFactory->val($nodeValue->methodName)); + } } diff --git a/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php new file mode 100644 index 00000000000..378933c20da --- /dev/null +++ b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php @@ -0,0 +1,77 @@ +> + */ + public function getNodeTypes(): array + { + return [Class_::class]; + } + + /** + * @param Class_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->isObjectType($node, new ObjectType('Spatie\Enum\Enum'))) { + return null; + } + + return $this->enumFactory->createFromSpatieClass($node); + } +}