Skip to content

Commit

Permalink
feature: replace MyCLabs Enum constructor call
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosvinicius committed Dec 14, 2024
1 parent 15fcd52 commit ca5a6d2
Show file tree
Hide file tree
Showing 11 changed files with 290 additions and 0 deletions.
2 changes: 2 additions & 0 deletions config/set/php81.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Rector\Php81\Rector\FuncCall\NullToStrictStringFuncCallArgRector;
use Rector\Php81\Rector\MethodCall\MyCLabsMethodCallToEnumConstRector;
use Rector\Php81\Rector\MethodCall\SpatieEnumMethodCallToEnumConstRector;
use Rector\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector;
use Rector\Php81\Rector\Property\ReadOnlyPropertyRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnNeverTypeRector;

Expand All @@ -18,6 +19,7 @@
ReturnNeverTypeRector::class,
MyCLabsClassToEnumRector::class,
MyCLabsMethodCallToEnumConstRector::class,
MyCLabsConstructorCallToEnumFromRector::class,
ReadOnlyPropertyRector::class,
SpatieEnumClassToEnumRector::class,
SpatieEnumMethodCallToEnumConstRector::class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use MyCLabs\Enum\Enum;

class EnumWithSelfCreation extends Enum
{
private const VALUE = 'value';

public static function newSelf(): self
{
return new self('value');
}

public static function newStatic(): static
{
return new static('value');
}
}
?>
-----
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use MyCLabs\Enum\Enum;

class EnumWithSelfCreation extends Enum
{
private const VALUE = 'value';

public static function newSelf(): self
{
return Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture\EnumWithSelfCreation::from('value');
}

public static function newStatic(): static
{
return Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture\EnumWithSelfCreation::from('value');
}
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source\SomeEnum;

class RefactorDefaultConstructorCall
{
public function someMethod(): SomeEnum
{
return new SomeEnum('value');
}
}
?>
-----
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source\SomeEnum;

class RefactorDefaultConstructorCall
{
public function someMethod(): SomeEnum
{
return Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source\SomeEnum::from('value');
}
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source\SomeEnumWithConstructor;

class SkipConstructorCallForCustomConstructor
{
public function someMethod(): SomeEnumWithConstructor
{
return new SomeEnumWithConstructor('value');
}
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Fixture;

use Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source\SomeClass;

class SkipConstructorCallForNonEnum
{
public function someMethod(): SomeClass
{
return new SomeClass();
}
}
?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class MyCLabsConstructorCallToEnumFromRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::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,9 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source;

final class SomeClass
{
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source;

use MyCLabs\Enum\Enum;

/**
* @method SomeEnum VALUE()
*/
final class SomeEnum extends Enum
{
const VALUE = 'value';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\Source;

use MyCLabs\Enum\Enum;

/**
* @method SomeEnumWithConstructor VALUE()
*/
final class SomeEnumWithConstructor extends Enum
{
const VALUE = 'value';

public function __construct($value)
{
//some logic
parent::__construct($value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector;

return RectorConfig::configure()
->withRules([MyCLabsConstructorCallToEnumFromRector::class]);
106 changes: 106 additions & 0 deletions rules/Php81/Rector/New_/MyCLabsConstructorCallToEnumFromRector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

namespace Rector\Php81\Rector\New_;

use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use Rector\Enum\ObjectReference;
use Rector\Rector\AbstractRector;
use Rector\ValueObject\MethodName;
use Rector\ValueObject\PhpVersionFeature;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;

/**
* @see \Rector\Tests\Php81\Rector\New_\MyCLabsConstructorCallToEnumFromRector\MyCLabsConstructorCallToEnumFromRectorTest
*/
final class MyCLabsConstructorCallToEnumFromRector extends AbstractRector implements MinPhpVersionInterface
{
private const MY_C_LABS_CLASS = 'MyCLabs\Enum\Enum';

private const DEFAULT_ENUM_CONSTRUCTOR = 'from';

public function __construct(
private readonly ReflectionProvider $reflectionProvider,
) {
}

/**
* @return array<class-string<Node>>
*/
public function getNodeTypes(): array
{
return [New_::class];
}

/**
* @param New_ $node
*/
public function refactor(Node $node): ?Node
{
return $this->refactorConstructorCallToStaticFromCall($node);
}

public function provideMinPhpVersion(): int
{
return PhpVersionFeature::ENUM;
}

public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Refactor MyCLabs Enum using constructor for instantiation',
[
new CodeSample(
<<<'CODE_SAMPLE'
$enum = new Enum($args);
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
$enum = Enum::from($args);
CODE_SAMPLE
)]
);
}

private function refactorConstructorCallToStaticFromCall(New_ $node): ?StaticCall
{
if (! $this->isObjectType($node->class, new ObjectType(self::MY_C_LABS_CLASS))) {
return null;
}

$classname = $this->getName($node->class);
if (in_array($classname, [ObjectReference::SELF, ObjectReference::STATIC], true)) {
$classname = $node->getAttribute('scope')?->getClassReflection()?->getDisplayName();
}

if ($classname === null) {
return null;
}

if (! $this->isMyCLabsConstructor($node, $classname)) {
return null;
}

return new StaticCall(new Name($classname), self::DEFAULT_ENUM_CONSTRUCTOR, $node->getArgs());
}

private function isMyCLabsConstructor(New_ $node, string $classname): bool
{
$class_reflection = $this->reflectionProvider->getClass($classname);
if (! $class_reflection->hasMethod(MethodName::CONSTRUCT)) {
return true;
}

$scope = $node->getAttribute('scope');
return $scope && $class_reflection
->getMethod(MethodName::CONSTRUCT, $scope)
->getDeclaringClass()
->getName() === self::MY_C_LABS_CLASS;
}
}

0 comments on commit ca5a6d2

Please sign in to comment.