diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index e762bb2b4b4..9d90053bab7 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -44,8 +44,7 @@ jobs: --skip-type="Rector\\NodeTypeResolver\\PHPStan\\Scope\\Contract\\NodeVisitor\\ScopeResolverNodeVisitorInterface" \ --skip-type="Rector\\BetterPhpDocParser\\Contract\\BasePhpDocNodeVisitorInterface" \ --skip-type="Rector\\BetterPhpDocParser\\Contract\\PhpDocParser\\PhpDocNodeDecoratorInterface" \ - --skip-type="Rector\\BetterPhpDocParser\\ValueObject\\Type\\FullyQualifiedIdentifierTypeNode" \ - --skip-type="Rector\\Contract\\Rector\\CollectorRectorInterface" + --skip-type="Rector\\BetterPhpDocParser\\ValueObject\\Type\\FullyQualifiedIdentifierTypeNode" - name: 'Compatible PHPStan versions' diff --git a/build/target-repository/docs/rector_rules_overview.md b/build/target-repository/docs/rector_rules_overview.md index 40977587831..e90a4d280fd 100644 --- a/build/target-repository/docs/rector_rules_overview.md +++ b/build/target-repository/docs/rector_rules_overview.md @@ -1,4 +1,4 @@ -# 355 Rules Overview +# 354 Rules Overview
@@ -46,7 +46,7 @@ - [Php83](#php83) (3) -- [Privatization](#privatization) (5) +- [Privatization](#privatization) (4) - [Removing](#removing) (5) @@ -5136,6 +5136,8 @@ Decorate read-only property with `readonly` attribute Refactor Spatie enum class to native Enum +:wrench: **configure it!** + - class: [`Rector\Php81\Rector\Class_\SpatieEnumClassToEnumRector`](../rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php) ```diff @@ -5304,25 +5306,6 @@ Combine separated host and port on `ldap_connect()` args ## Privatization -### FinalizeClassesWithoutChildrenCollectorRector - -Finalize classes without children using collectors - -- class: [`Rector\Privatization\Rector\Class_\FinalizeClassesWithoutChildrenCollectorRector`](../rules/Privatization/Rector/Class_/FinalizeClassesWithoutChildrenCollectorRector.php) - -```diff --class FirstClass extends SecondClass -+final class FirstClass extends SecondClass - { - } - - class SecondClass - { - } -``` - -
- ### FinalizeClassesWithoutChildrenRector Finalize every class that has no children diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc new file mode 100644 index 00000000000..4d0f4d06ff0 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/FixtureUpperCaseSnakeCase/enum_with_camel_case_names.php.inc @@ -0,0 +1,33 @@ + +----- + diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php new file mode 100644 index 00000000000..5fb53a48397 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/UpperCaseSnakeCaseTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureUpperCaseSnakeCase'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule_uppercase_snake_case.php'; + } +} diff --git a/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php new file mode 100644 index 00000000000..67341b53445 --- /dev/null +++ b/rules-tests/Php81/Rector/Class_/SpatieEnumClassToEnumRector/config/configured_rule_uppercase_snake_case.php @@ -0,0 +1,12 @@ +ruleWithConfiguration(SpatieEnumClassToEnumRector::class, [ + SpatieEnumClassToEnumRector::TO_UPPER_SNAKE_CASE => true, + ]); +}; diff --git a/rules/Php81/NodeFactory/EnumFactory.php b/rules/Php81/NodeFactory/EnumFactory.php index 70336a115a0..789991cbea5 100644 --- a/rules/Php81/NodeFactory/EnumFactory.php +++ b/rules/Php81/NodeFactory/EnumFactory.php @@ -36,6 +36,12 @@ */ private const PASCAL_CASE_TO_UNDERSCORE_REGEX = '/(?<=[A-Z])(?=[A-Z][a-z])|(?<=[^A-Z])(?=[A-Z])|(?<=[A-Za-z])(?=[^A-Za-z])/'; + /** + * @var string + * @see https://regex101.com/r/FneU33/1 + */ + private const MULTI_UNDERSCORES_REGEX = '#_{2,}#'; + public function __construct( private NodeNameResolver $nodeNameResolver, private PhpDocInfoFactory $phpDocInfoFactory, @@ -142,6 +148,7 @@ private function createEnumCaseFromDocComment( $enumName = strtoupper( Strings::replace($nodeValue->methodName, self::PASCAL_CASE_TO_UNDERSCORE_REGEX, '_$0') ); + $enumName = Strings::replace($enumName, self::MULTI_UNDERSCORES_REGEX, '_'); } else { $enumName = strtoupper($nodeValue->methodName); } diff --git a/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php index 25f76d540b1..133a317315d 100644 --- a/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php +++ b/rules/Php81/Rector/Class_/SpatieEnumClassToEnumRector.php @@ -13,7 +13,7 @@ use Rector\Rector\AbstractRector; use Rector\ValueObject\PhpVersionFeature; use Rector\VersionBonding\Contract\MinPhpVersionInterface; -use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; +use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; /** @@ -26,6 +26,11 @@ final class SpatieEnumClassToEnumRector extends AbstractRector implements MinPhp { private bool $toUpperSnakeCase = false; + /** + * @var string + */ + public const TO_UPPER_SNAKE_CASE = 'toUpperSnakeCase'; + public function __construct( private readonly EnumFactory $enumFactory ) { @@ -39,7 +44,7 @@ public function provideMinPhpVersion(): int public function getRuleDefinition(): RuleDefinition { return new RuleDefinition('Refactor Spatie enum class to native Enum', [ - new CodeSample( + new ConfiguredCodeSample( <<<'CODE_SAMPLE' use \Spatie\Enum\Enum; @@ -61,7 +66,10 @@ enum StatusEnum : string case PUBLISHED = 'published'; case ARCHIVED = 'archived'; } -CODE_SAMPLE +CODE_SAMPLE, +[ + SpatieEnumClassToEnumRector::TO_UPPER_SNAKE_CASE => false, +] ), ]); } @@ -91,6 +99,6 @@ public function refactor(Node $node): ?Enum_ */ public function configure(array $configuration): void { - $this->toUpperSnakeCase = true === ($configuration['toUpperSnakeCase'] ?? false); + $this->toUpperSnakeCase = $configuration[self::TO_UPPER_SNAKE_CASE] ?? false; } } diff --git a/src/Config/RectorConfig.php b/src/Config/RectorConfig.php index 1100055d993..4e621e67160 100644 --- a/src/Config/RectorConfig.php +++ b/src/Config/RectorConfig.php @@ -9,7 +9,6 @@ use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface; use Rector\Configuration\Option; use Rector\Configuration\Parameter\SimpleParameterProvider; -use Rector\Contract\Rector\CollectorRectorInterface; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\DependencyInjection\Laravel\ContainerMemento; @@ -187,10 +186,6 @@ public function rule(string $rectorClass): void $this->singleton($rectorClass); $this->tag($rectorClass, RectorInterface::class); - if (is_a($rectorClass, CollectorRectorInterface::class, true)) { - $this->tag($rectorClass, CollectorRectorInterface::class); - } - // for cache invalidation in case of change SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass); } diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index cce6269202d..6e33c906144 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -58,7 +58,6 @@ use Rector\Console\Style\RectorStyle; use Rector\Console\Style\SymfonyStyleFactory; use Rector\Contract\DependencyInjection\ResetableInterface; -use Rector\Contract\Rector\CollectorRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\NodeDecorator\CreatedByRuleDecorator; use Rector\NodeNameResolver\Contract\NodeNameResolverInterface; @@ -435,10 +434,6 @@ public function create(): RectorConfig ->needs('$rectors') ->giveTagged(RectorInterface::class); - $rectorConfig->when(RectorNodeTraverser::class) - ->needs('$collectorRectors') - ->giveTagged(CollectorRectorInterface::class); - $rectorConfig->when(ConfigInitializer::class) ->needs('$rectors') ->giveTagged(RectorInterface::class); diff --git a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php index b498fcd869e..61a05242049 100644 --- a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php @@ -6,7 +6,6 @@ use PhpParser\Node; use PhpParser\NodeTraverser; -use Rector\Contract\Rector\CollectorRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\VersionBonding\PhpVersionedFilter; @@ -60,15 +59,7 @@ private function prepareNodeVisitors(): void } // filer out by version - $activeRectors = $this->phpVersionedFilter->filter($this->rectors); - - $nonCollectorActiveRectors = array_filter( - $activeRectors, - static fn (RectorInterface $rector): bool => ! $rector instanceof CollectorRectorInterface - ); - - $this->visitors = [...$this->visitors, ...$nonCollectorActiveRectors]; - + $this->visitors = $this->phpVersionedFilter->filter($this->rectors); $this->areNodeVisitorsPrepared = true; } } diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php index 2d1f5fe6473..16eaa86d123 100644 --- a/src/Testing/PHPUnit/AbstractRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -8,7 +8,6 @@ use Iterator; use Nette\Utils\FileSystem; use Nette\Utils\Strings; -use PHPStan\Collectors\Collector; use PHPUnit\Framework\ExpectationFailedException; use Rector\Application\ApplicationFileProcessor; use Rector\Autoloading\AdditionalAutoloader; @@ -17,7 +16,6 @@ use Rector\Configuration\Option; use Rector\Configuration\Parameter\SimpleParameterProvider; use Rector\Contract\DependencyInjection\ResetableInterface; -use Rector\Contract\Rector\CollectorRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\DependencyInjection\Laravel\ContainerMemento; use Rector\Exception\ShouldNotHappenException; @@ -89,8 +87,6 @@ protected function setUp(): void // this has to be always empty, so we can add new rules with their configuration $this->assertEmpty($rectorConfig->tagged(RectorInterface::class)); - $this->assertEmpty($rectorConfig->tagged(CollectorRectorInterface::class)); - $this->assertEmpty($rectorConfig->tagged(Collector::class)); $this->bootFromConfigFiles([$configFile]); @@ -174,8 +170,6 @@ protected function forgetRectorsRulesAndCollectors(): void // 1. forget tagged services ContainerMemento::forgetTag($rectorConfig, RectorInterface::class); - ContainerMemento::forgetTag($rectorConfig, Collector::class); - ContainerMemento::forgetTag($rectorConfig, CollectorRectorInterface::class); // 2. remove after binding too, to avoid setting configuration over and over again $privatesAccessor = new PrivatesAccessor(); diff --git a/stubs/PHPStan/Collectors/Collector.php b/stubs/PHPStan/Collectors/Collector.php deleted file mode 100644 index 385cd0c6b45..00000000000 --- a/stubs/PHPStan/Collectors/Collector.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ - - public function getNodeType() : string; - /** - * @phpstan-param TNodeType $node - * @return TValue|null Collected data - */ - public function processNode(Node $node, Scope $scope); -}