Skip to content

Commit

Permalink
[PHP 8.1] Add basic support for Spatie Enums to native Enums (#653)
Browse files Browse the repository at this point in the history
* Add basic support for spatie Enums

* move spatie/enum to require-dev

* Update rules/Php81/NodeFactory/EnumFactory.php

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* Update rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* Update rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc

Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>

* Update rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc

* Update rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/Fixture/some_class.php.inc

* Update rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php

Co-authored-by: Tomas Votruba <tomas.vot@gmail.com>
Co-authored-by: Abdul Malik Ikhsan <samsonasik@gmail.com>
  • Loading branch information
3 people committed Aug 12, 2021
1 parent 8e48e49 commit 48121fb
Show file tree
Hide file tree
Showing 6 changed files with 190 additions and 4 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector\Fixture;

use Spatie\Enum\Enum;

/**
* @method static self draft()
* @method static self published()
* @method static self archived()
*/
class StatusEnum extends Enum
{
}

?>
-----
<?php

namespace Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector\Fixture;

use Spatie\Enum\Enum;

enum StatusEnum
{
case draft = 'draft';
case published = 'published';
case archived = 'archived';
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector;

use Iterator;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;

final class SpatieEnumClassToEnumRectorTest 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';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

use Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(SpatieEnumClassToEnumRector::class);
};
41 changes: 37 additions & 4 deletions rules/Php81/NodeFactory/EnumFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
}

Expand All @@ -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));
}
}
77 changes: 77 additions & 0 deletions rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

declare(strict_types=1);

namespace Rector\Php81\Rector\Class_;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\Php81\NodeFactory\EnumFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @changelog https://wiki.php.net/rfc/enumerations
* @changelog https://github.com/spatie/enum
*
* @see \Rector\Tests\Php81\Rector\Class_\SpatieEnumClassToEnumRector\SpatieEnumClassToEnumRectorTest
*/
final class SpatieEnumClassToEnumRector extends AbstractRector
{
public function __construct(
private EnumFactory $enumFactory
) {
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition('Refactor Spatie enum class to native Enum', [
new CodeSample(
<<<'CODE_SAMPLE'
use \Spatie\Enum\Enum;
/**
* @method static self draft()
* @method static self published()
* @method static self archived()
*/
class StatusEnum extends Enum
{
}
CODE_SAMPLE

,
<<<'CODE_SAMPLE'
enum StatusEnum
{
case draft = 'draft';
case published = 'published';
case archived = 'archived';
}
CODE_SAMPLE
),
]);
}

/**
* @return array<class-string<Node>>
*/
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);
}
}

0 comments on commit 48121fb

Please sign in to comment.