From 630dafce8ae4c880b3c4c618e5c26f3f7234d9e0 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sat, 22 Aug 2026 22:43:53 +0100 Subject: [PATCH] Switch DI container from illuminate/container to entropy/entropy Replace the Illuminate container with entropy/entropy. RectorConfig extends Entropy\Container\Container and reuses the container's own primitives: - register() makes a factory-less service discoverable by contract, so findByContract() returns it without any tag layer (tag/tagged/autotag gone) - findByContract() returns a plain 0-indexed list, so variadic spreads such as new NodeTraverser(...$visitors) stay positional - afterResolving() runs setter injection once after a service is built, drained at the outermost make() so aggregate cycles (type mappers <-> the collection they belong to) resolve without a loop - forgetByContract() drops a service from the container; RectorConfig extends it to clear its own bookkeeping, so skip()/test reset need no reflection Removed along the way: the giveTagged() contextual-binding API, service aliases (the one alias was already backed by a factory), the RegisteredService value object, and the ContainerMemento reflection helper. No WeakMap, to keep the source downgradable; service build failures propagate as entropy throws them. --- composer.json | 15 +- .../ClassNameImportSkipper.php | 17 +- src/Config/RectorConfig.php | 102 +++++++-- src/Config/RegisteredService.php | 30 --- src/Configuration/RectorConfigBuilder.php | 27 +-- .../Output/OutputFormatterCollector.php | 2 +- .../Laravel/ContainerMemento.php | 58 ----- .../LazyContainerFactory.php | 208 +++++------------- src/NodeNameResolver/NodeNameResolver.php | 2 +- src/NodeTypeResolver/NodeTypeResolver.php | 2 +- .../Scope/PHPStanNodeScopeResolver.php | 2 +- .../Mapper/PhpParserNodeMapper.php | 2 +- .../PHPUnit/AbstractRectorTestCase.php | 42 +--- src/Util/Reflection/PrivatesAccessor.php | 13 -- src/functions/node_helper.php | 7 +- tests/Bin/RectorTest.php | 4 +- tests/Configuration/OnlyRuleResolverTest.php | 2 +- .../TaggedServicesTest.php | 2 +- .../Issue9388/config/configured_rule.php | 9 +- .../TypeMapperOrderTest.php | 4 +- .../Skipper/Skipper/SkipperRectorRuleTest.php | 7 +- 21 files changed, 171 insertions(+), 386 deletions(-) delete mode 100644 src/Config/RegisteredService.php delete mode 100644 src/DependencyInjection/Laravel/ContainerMemento.php diff --git a/composer.json b/composer.json index 183e9a62790..7df76cb23d9 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.5", "doctrine/inflector": "^2.1", - "illuminate/container": "12.39.*", + "entropy/entropy": "^0.4.9", "nette/utils": "^4.1.4", "nikic/php-parser": "^5.8", "ondram/ci-detector": "^4.2", @@ -55,7 +55,6 @@ "symplify/easy-coding-standard": "^13.2.13", "symplify/phpstan-extensions": "^12.0.2", "symplify/phpstan-rules": "^14.12", - "symplify/vendor-patches": "^11.5", "tomasvotruba/class-leak": "^2.1", "tomasvotruba/fast-unit": "^0.1", "tomasvotruba/type-coverage": "^2.3", @@ -115,22 +114,12 @@ "preload": "php build/build-preload.php .", "release": "vendor/bin/rng --from-commit X --to-commit Y --remote-repository rectorphp/rector-symfony --remote-repository rectorphp/rector-doctrine --remote-repository rectorphp/rector-phpunit" }, - "extra": { - "patches": { - "illuminate/container": [ - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/illuminate-container-container-php.patch" - ] - }, - "composer-exit-on-patch-failure": true, - "enable-patching": true - }, "config": { "sort-packages": true, "platform-check": false, "allow-plugins": { "phpstan/extension-installer": true, - "rector/extension-installer": true, - "cweagans/composer-patches": true + "rector/extension-installer": true } }, "minimum-stability": "dev", diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php index 5812d47fa10..0e3d4fb01ba 100644 --- a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipper.php @@ -22,23 +22,14 @@ * @param ClassNameImportSkipVoterInterface[] $classNameImportSkipVoters */ public function __construct( - private iterable $classNameImportSkipVoters, + private array $classNameImportSkipVoters, private UseImportsResolver $useImportsResolver, ) { } - public function shouldSkipNameForFullyQualifiedObjectType( - File $file, - Node $node, - FullyQualifiedObjectType $fullyQualifiedObjectType - ): bool { - foreach ($this->classNameImportSkipVoters as $classNameImportSkipVoter) { - if ($classNameImportSkipVoter->shouldSkip($file, $fullyQualifiedObjectType, $node)) { - return true; - } - } - - return false; + public function shouldSkipNameForFullyQualifiedObjectType(File $file, Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool + { + return array_any($this->classNameImportSkipVoters, fn (ClassNameImportSkipVoterInterface $classNameImportSkipVoter): bool => $classNameImportSkipVoter->shouldSkip($file, $fullyQualifiedObjectType, $node)); } /** diff --git a/src/Config/RectorConfig.php b/src/Config/RectorConfig.php index 5fdeb2c7251..b45d6f678e5 100644 --- a/src/Config/RectorConfig.php +++ b/src/Config/RectorConfig.php @@ -6,7 +6,7 @@ use Composer\Semver\Semver; use Deprecated; -use Illuminate\Container\Container; +use Entropy\Container\Container; use Override; use Rector\Caching\Contract\ValueObject\Storage\CacheStorageInterface; use Rector\Composer\InstalledPackageResolver; @@ -14,10 +14,8 @@ use Rector\Configuration\Parameter\SimpleParameterProvider; use Rector\Configuration\RectorConfigBuilder; use Rector\Contract\DependencyInjection\RelatedConfigInterface; -use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\Contract\Rector\RectorInterface; -use Rector\DependencyInjection\Laravel\ContainerMemento; use Rector\Enum\Config\Defaults; use Rector\Exception\ShouldNotHappenException; use Rector\Skipper\SkipCriteriaResolver\SkippedClassResolver; @@ -51,14 +49,23 @@ final class RectorConfig extends Container private array $registeredComposerBoundRuleConfigurations = []; /** - * @var string[] + * Optional override, e.g. injected by a test to read the versions from a standalone "composer.json" */ - private array $autotagInterfaces = [Command::class, ResettableInterface::class]; + private ?InstalledPackageResolver $installedPackageResolver = null; /** - * Optional override, e.g. injected by a test to read the versions from a standalone "composer.json" + * Explicitly registered service ids, used for bound() and to drive forgetting on skip()/reset. + * + * @var array */ - private ?InstalledPackageResolver $installedPackageResolver = null; + private array $boundAbstracts = []; + + /** + * Service ids that got a factory closure registered on the entropy container. + * + * @var array + */ + private array $factoryBound = []; private static ?bool $recreated = null; @@ -215,8 +222,13 @@ public function ruleWithConfiguration(string $rectorClass, array $configuration) $this->afterResolving($rectorClass, function (ConfigurableRectorInterface $configurableRector) use ( $rectorClass ): void { - $ruleConfiguration = $this->ruleConfigurations[$rectorClass]; - $configurableRector->configure($ruleConfiguration); + // the rule may have been re-registered without configuration since this callback was + // queued (e.g. a later test reusing the rule via a set), so skip when it has no config + if (! isset($this->ruleConfigurations[$rectorClass])) { + return; + } + + $configurableRector->configure($this->ruleConfigurations[$rectorClass]); }); } @@ -274,13 +286,11 @@ public function rule(string $rectorClass): void $this->singleton($rectorClass); - // the same rule can be registered by multiple sets, tag it only once, + // the same rule can be registered by multiple sets, record it only once, // otherwise it is run twice on every node and listed twice in the reports if (! isset($this->registeredRectorClasses[$rectorClass])) { $this->registeredRectorClasses[$rectorClass] = true; - $this->tag($rectorClass, RectorInterface::class); - // for cache invalidation in case of change SimpleParameterProvider::addParameter(Option::REGISTERED_RECTOR_RULES, $rectorClass); } @@ -304,7 +314,6 @@ public function rule(string $rectorClass): void public function command(string $commandClass): void { $this->singleton($commandClass); - $this->tag($commandClass, Command::class); } public function import(string $filePath): void @@ -500,32 +509,77 @@ public function boot(): void } // completely forget the Rector rule only when no path specified - ContainerMemento::forgetService($this, $skippedClass); + $this->forgetByContract($skippedClass); } } /** - * @internal Use to add tag on service registrations + * Register a shared service. Without a $concrete factory the entropy container autowires the + * class on demand via reflection; register() makes it discoverable by the interfaces it + * implements, so findByContract() can find it without any explicit tagging. + * + * @param class-string $abstract + * @param (callable(self): object)|null $concrete */ - public function autotagInterface(string $interface): void + public function singleton(string $abstract, ?callable $concrete = null): void { - $this->autotagInterfaces[] = $interface; + $this->boundAbstracts[$abstract] = true; + + if ($concrete === null) { + // no factory: let the entropy container discover it by contract + $this->register($abstract); + return; + } + + if (! isset($this->factoryBound[$abstract])) { + $this->factoryBound[$abstract] = true; + // entropy calls the factory with the container instance, which is always this RectorConfig + parent::service($abstract, fn (): object => $concrete($this)); + } + } + + /** + * PSR-11 style accessor, kept for call sites that read services eagerly. + * + * @template TObject of object + * @param class-string $id + * @return TObject + */ + public function get(string $id): object + { + return $this->make($id); } /** - * @param string $abstract + * @param class-string $abstract + */ + public function bound(string $abstract): bool + { + return isset($this->boundAbstracts[$abstract]); + } + + /** + * Forget every service of the contract, both from the entropy container and from the local + * bookkeeping, so a skipped or reset service is not seen as bound and cannot be resurrected + * through discovery. + * + * @param class-string $contract */ #[Override] - public function singleton($abstract, mixed $concrete = null): void + public function forgetByContract(string $contract): void { - parent::singleton($abstract, $concrete); + parent::forgetByContract($contract); - foreach ($this->autotagInterfaces as $autotagInterface) { - if (! is_a($abstract, $autotagInterface, true)) { + foreach (array_keys($this->boundAbstracts) as $abstract) { + if (! is_a($abstract, $contract, true)) { continue; } - $this->tag($abstract, $autotagInterface); + unset( + $this->boundAbstracts[$abstract], + $this->factoryBound[$abstract], + $this->registeredRectorClasses[$abstract], + ); } } @@ -559,7 +613,7 @@ public function getRuleConfigurations(): array */ public function getMainRectorClasses(): array { - return $this->tags[RectorInterface::class] ?? []; + return array_keys($this->registeredRectorClasses); } /** diff --git a/src/Config/RegisteredService.php b/src/Config/RegisteredService.php deleted file mode 100644 index e94efece663..00000000000 --- a/src/Config/RegisteredService.php +++ /dev/null @@ -1,30 +0,0 @@ -className; - } - - public function getAlias(): ?string - { - return $this->alias; - } - - public function getTag(): ?string - { - return $this->tag; - } -} diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index cb62b024e18..def00575e03 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -16,11 +16,9 @@ use Rector\Config\Level\TypeDeclarationDocblocksLevel; use Rector\Config\Level\TypeDeclarationLevel; use Rector\Config\RectorConfig; -use Rector\Config\RegisteredService; use Rector\Configuration\Levels\LevelRulesResolver; use Rector\Configuration\Parameter\SimpleParameterProvider; use Rector\Console\Notifier; -use Rector\Contract\PhpParser\DecoratingNodeVisitorInterface; use Rector\Contract\Rector\ConfigurableRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\Doctrine\Set\DoctrineSetList; @@ -173,7 +171,7 @@ final class RectorConfigBuilder private array $typeGuardedClasses = []; /** - * @var RegisteredService[] + * @var array */ private array $registerServices = []; @@ -280,15 +278,7 @@ public function __invoke(RectorConfig $rectorConfig): void // must be in upper part, as these services might be used by rule registered bellow foreach ($this->registerServices as $registerService) { - $rectorConfig->singleton($registerService->getClassName()); - - if ($registerService->getAlias()) { - $rectorConfig->alias($registerService->getClassName(), $registerService->getAlias()); - } - - if ($registerService->getTag()) { - $rectorConfig->tag($registerService->getClassName(), $registerService->getTag()); - } + $rectorConfig->singleton($registerService); } if ($this->skip !== []) { @@ -1114,9 +1104,12 @@ public function withTypeGuardedClasses(array $typeGuardedClasses): self return $this; } - public function registerService(string $className, ?string $alias = null, ?string $tag = null): self + /** + * @param class-string $className + */ + public function registerService(string $className): self { - $this->registerServices[] = new RegisteredService($className, $alias, $tag); + $this->registerServices[] = $className; return $this; } @@ -1130,11 +1123,7 @@ public function registerDecoratingNodeVisitor(string $decoratingNodeVisitorClass { Assert::isAOf($decoratingNodeVisitorClass, NodeVisitor::class); - $this->registerServices[] = new RegisteredService( - $decoratingNodeVisitorClass, - null, - DecoratingNodeVisitorInterface::class - ); + $this->registerServices[] = $decoratingNodeVisitorClass; return $this; } diff --git a/src/Console/Output/OutputFormatterCollector.php b/src/Console/Output/OutputFormatterCollector.php index 8102612d3b9..bd8abbf0c5f 100644 --- a/src/Console/Output/OutputFormatterCollector.php +++ b/src/Console/Output/OutputFormatterCollector.php @@ -17,7 +17,7 @@ final class OutputFormatterCollector /** * @param OutputFormatterInterface[] $outputFormatters */ - public function __construct(iterable $outputFormatters) + public function __construct(array $outputFormatters) { foreach ($outputFormatters as $outputFormatter) { $this->outputFormatters[$outputFormatter->getName()] = $outputFormatter; diff --git a/src/DependencyInjection/Laravel/ContainerMemento.php b/src/DependencyInjection/Laravel/ContainerMemento.php deleted file mode 100644 index 607edcf8fad..00000000000 --- a/src/DependencyInjection/Laravel/ContainerMemento.php +++ /dev/null @@ -1,58 +0,0 @@ -tagged($tagToForget); - foreach ($taggedClasses as $taggedClass) { - $container->offsetUnset($taggedClass::class); - } - - // 2. forget tagged references - $privatesAccessor = new PrivatesAccessor(); - $privatesAccessor->propertyClosure($container, 'tags', static function (array $tags) use ( - $tagToForget - ): array { - unset($tags[$tagToForget]); - return $tags; - }); - } - - public static function forgetService(Container $container, string $typeToForget): void - { - // 1. remove the service - $container->offsetUnset($typeToForget); - - // 2. remove all tagged rules - $privatesAccessor = new PrivatesAccessor(); - $privatesAccessor->propertyClosure($container, 'tags', static function (array $tags) use ( - $typeToForget - ): array { - foreach ($tags as $tagName => $taggedClasses) { - foreach ($taggedClasses as $key => $taggedClass) { - if (is_a($taggedClass, $typeToForget, true)) { - unset($tags[$tagName][$key]); - } - } - } - - return $tags; - }); - } -} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index 308baf431f5..695e9c9a312 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -6,7 +6,6 @@ use Doctrine\Inflector\Inflector; use Doctrine\Inflector\Rules\English\InflectorFactory; -use Illuminate\Container\Container; use PhpParser\Lexer; use PHPStan\Analyser\NodeScopeResolver; use PHPStan\Analyser\ScopeFactory; @@ -20,14 +19,12 @@ use Rector\BetterPhpDocParser\Comment\CommentsMerger; use Rector\BetterPhpDocParser\Contract\BasePhpDocNodeVisitorInterface; use Rector\BetterPhpDocParser\Contract\PhpDocParser\PhpDocNodeDecoratorInterface; -use Rector\BetterPhpDocParser\PhpDocNodeMapper; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\ArrayTypePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\CallableTypePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\IntersectionTypeNodePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\TemplatePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\UnionTypeNodePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocParser\ArrayItemClassNameDecorator; -use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser; use Rector\BetterPhpDocParser\PhpDocParser\ConstExprClassNameDecorator; use Rector\BetterPhpDocParser\PhpDocParser\DoctrineAnnotationDecorator; use Rector\BetterPhpDocParser\PhpDocParser\PhpDocTagGenericUsesDecorator; @@ -41,7 +38,6 @@ use Rector\ChangesReporting\Output\GitHubOutputFormatter; use Rector\ChangesReporting\Output\GitlabOutputFormatter; use Rector\ChangesReporting\Output\JsonOutputFormatter; -use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\AliasClassNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\ClassLikeNameClassNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\FullyQualifiedNameClassNameImportSkipVoter; @@ -51,9 +47,7 @@ use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\UsesClassNameImportSkipVoter; use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface; use Rector\Config\RectorConfig; -use Rector\Configuration\ConfigInitializer; use Rector\Configuration\ConfigurationRuleFilter; -use Rector\Configuration\OnlyRuleResolver; use Rector\Configuration\RenamedClassesDataCollector; use Rector\Console\Command\ComposerBasedCommand; use Rector\Console\Command\CustomRuleCommand; @@ -62,12 +56,8 @@ use Rector\Console\Command\SetupCICommand; use Rector\Console\Command\WorkerCommand; use Rector\Console\ConsoleApplication; -use Rector\Console\Output\OutputFormatterCollector; -use Rector\Console\Style\RectorStyle; use Rector\Console\Style\SymfonyStyleFactory; -use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Contract\PhpParser\DecoratingNodeVisitorInterface; -use Rector\Contract\Rector\RectorInterface; use Rector\NodeDecorator\CreatedByRuleDecorator; use Rector\NodeNameResolver\Contract\NodeNameResolverInterface; use Rector\NodeNameResolver\NodeNameResolver; @@ -96,12 +86,10 @@ use Rector\NodeTypeResolver\NodeTypeResolver\ScalarTypeResolver; use Rector\NodeTypeResolver\NodeTypeResolver\StaticCallMethodCallTypeResolver; use Rector\NodeTypeResolver\NodeTypeResolver\TraitTypeResolver; -use Rector\NodeTypeResolver\PHPStan\Scope\PHPStanNodeScopeResolver; use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider; use Rector\Php80\AttributeDecorator\DoctrineConverterAttributeDecorator; use Rector\Php80\AttributeDecorator\SensioParamConverterAttributeDecorator; use Rector\Php80\Contract\ConverterAttributeDecoratorInterface; -use Rector\Php80\NodeManipulator\AttributeGroupNamedArgumentManipulator; use Rector\PhpAttribute\AnnotationToAttributeMapper; use Rector\PhpAttribute\AnnotationToAttributeMapper\ArrayAnnotationToAttributeMapper; use Rector\PhpAttribute\AnnotationToAttributeMapper\ArrayItemNodeAnnotationToAttributeMapper; @@ -115,7 +103,6 @@ use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PhpParser\Comparing\NodeComparator; use Rector\PhpParser\Node\NodeFactory; -use Rector\PhpParser\NodeTraverser\RectorNodeTraverser; use Rector\PhpParser\NodeVisitor\ArgNodeVisitor; use Rector\PhpParser\NodeVisitor\ArgNotAcceptingClosureNodeVisitor; use Rector\PhpParser\NodeVisitor\AssignedToNodeVisitor; @@ -164,13 +151,10 @@ use Rector\PHPStanStaticTypeMapper\TypeMapper\VoidTypeMapper; use Rector\PostRector\Application\PostFileProcessor; use Rector\Rector\AbstractRector; -use Rector\Reporting\DeprecatedRulesReporter; use Rector\Skipper\Skipper\Skipper; use Rector\Skipper\Skipper\UsedSkipCollector; use Rector\StaticTypeMapper\Contract\PhpDocParser\PhpDocTypeMapperInterface; use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface; -use Rector\StaticTypeMapper\Mapper\PhpParserNodeMapper; -use Rector\StaticTypeMapper\PhpDoc\PhpDocTypeMapper; use Rector\StaticTypeMapper\PhpDocParser\IdentifierPhpDocTypeMapper; use Rector\StaticTypeMapper\PhpDocParser\IntersectionPhpDocTypeMapper; use Rector\StaticTypeMapper\PhpDocParser\NullablePhpDocTypeMapper; @@ -184,7 +168,6 @@ use Rector\StaticTypeMapper\PhpParser\StringNodeMapper; use Rector\StaticTypeMapper\PhpParser\UnionTypeNodeMapper; use Symfony\Component\Console\Application; -use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Style\SymfonyStyle; use Webmozart\Assert\Assert; @@ -404,8 +387,8 @@ public function create(): RectorConfig private function registerConsole(RectorConfig $rectorConfig): void { - $rectorConfig->singleton(Application::class, static function (Container $container): Application { - $consoleApplication = $container->make(ConsoleApplication::class); + $rectorConfig->singleton(Application::class, static function (RectorConfig $rectorConfig): Application { + $consoleApplication = $rectorConfig->make(ConsoleApplication::class); $commandNamesToHide = ['list', 'completion', 'help', 'worker']; foreach ($commandNamesToHide as $commandNameToHide) { @@ -416,10 +399,6 @@ private function registerConsole(RectorConfig $rectorConfig): void return $consoleApplication; }); - $rectorConfig->when(ConsoleApplication::class) - ->needs('$commands') - ->giveTagged(Command::class); - $rectorConfig->singleton(Inflector::class, static function (): Inflector { $inflectorFactory = new InflectorFactory(); return $inflectorFactory->build(); @@ -434,21 +413,6 @@ private function registerConsole(RectorConfig $rectorConfig): void $rectorConfig->singleton(CustomRuleCommand::class); $rectorConfig->singleton(ComposerBasedCommand::class); - $rectorConfig->when(ListRulesCommand::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); - - $rectorConfig->when(ComposerBasedCommand::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); - - $rectorConfig->when(OnlyRuleResolver::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); - - $rectorConfig->when(DeprecatedRulesReporter::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); } private function registerFileProcessing(RectorConfig $rectorConfig): void @@ -459,22 +423,10 @@ private function registerFileProcessing(RectorConfig $rectorConfig): void // shared state: collects used skips across the skipper, the path skipper and the file processor $rectorConfig->singleton(UsedSkipCollector::class); - $rectorConfig->when(RectorNodeTraverser::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); - - $rectorConfig->when(ConfigInitializer::class) - ->needs('$rectors') - ->giveTagged(RectorInterface::class); - - $rectorConfig->when(ClassNameImportSkipper::class) - ->needs('$classNameImportSkipVoters') - ->giveTagged(ClassNameImportSkipVoterInterface::class); - $rectorConfig->singleton( DynamicSourceLocatorProvider::class, - static function (Container $container): DynamicSourceLocatorProvider { - $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + static function (RectorConfig $rectorConfig): DynamicSourceLocatorProvider { + $phpStanServicesFactory = $rectorConfig->make(PHPStanServicesFactory::class); return $phpStanServicesFactory->createDynamicSourceLocatorProvider(); } ); @@ -482,15 +434,13 @@ static function (Container $container): DynamicSourceLocatorProvider { private function registerCachingAndResettables(RectorConfig $rectorConfig): void { - // resettable - // DynamicSourceLocatorProvider is autotagged on its singleton() call above, - // as RectorConfig autotags ResettableInterface - $rectorConfig->tag(RenamedClassesDataCollector::class, ResettableInterface::class); + // resettable: registering the class makes it discoverable via findByContract(ResettableInterface) + $rectorConfig->singleton(RenamedClassesDataCollector::class); // caching - $rectorConfig->singleton(Cache::class, static function (Container $container): Cache { + $rectorConfig->singleton(Cache::class, static function (RectorConfig $rectorConfig): Cache { /** @var CacheFactory $cacheFactory */ - $cacheFactory = $container->make(CacheFactory::class); + $cacheFactory = $rectorConfig->make(CacheFactory::class); return $cacheFactory->create(); }); } @@ -498,72 +448,44 @@ private function registerCachingAndResettables(RectorConfig $rectorConfig): void private function registerTypeMappers(RectorConfig $rectorConfig): void { // tagged services - $rectorConfig->when(BetterPhpDocParser::class) - ->needs('$phpDocNodeDecorators') - ->giveTagged(PhpDocNodeDecoratorInterface::class); - $rectorConfig->afterResolving( ArrayTypeMapper::class, - static function (ArrayTypeMapper $arrayTypeMapper, Container $container): void { - $arrayTypeMapper->autowire($container->make(PHPStanStaticTypeMapper::class)); + static function (ArrayTypeMapper $arrayTypeMapper) use ($rectorConfig): void { + $arrayTypeMapper->autowire($rectorConfig->make(PHPStanStaticTypeMapper::class)); } ); $rectorConfig->afterResolving( ConditionalTypeForParameterMapper::class, static function ( - ConditionalTypeForParameterMapper $conditionalTypeForParameterMapper, - Container $container - ): void { - $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + ConditionalTypeForParameterMapper $conditionalTypeForParameterMapper + ) use ($rectorConfig): void { + $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); $conditionalTypeForParameterMapper->autowire($phpStanStaticTypeMapper); } ); $rectorConfig->afterResolving( ConditionalTypeMapper::class, - static function (ConditionalTypeMapper $conditionalTypeMapper, Container $container): void { - $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + static function (ConditionalTypeMapper $conditionalTypeMapper) use ($rectorConfig): void { + $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); $conditionalTypeMapper->autowire($phpStanStaticTypeMapper); } ); $rectorConfig->afterResolving( UnionTypeMapper::class, - static function (UnionTypeMapper $unionTypeMapper, Container $container): void { - $phpStanStaticTypeMapper = $container->make(PHPStanStaticTypeMapper::class); + static function (UnionTypeMapper $unionTypeMapper) use ($rectorConfig): void { + $phpStanStaticTypeMapper = $rectorConfig->make(PHPStanStaticTypeMapper::class); $unionTypeMapper->autowire($phpStanStaticTypeMapper); } ); - $rectorConfig->when(PHPStanStaticTypeMapper::class) - ->needs('$typeMappers') - ->giveTagged(TypeMapperInterface::class); - - $rectorConfig->when(PhpDocTypeMapper::class) - ->needs('$phpDocTypeMappers') - ->giveTagged(PhpDocTypeMapperInterface::class); - - $rectorConfig->when(PhpParserNodeMapper::class) - ->needs('$phpParserNodeMappers') - ->giveTagged(PhpParserNodeMapperInterface::class); - - $rectorConfig->when(NodeTypeResolver::class) - ->needs('$nodeTypeResolvers') - ->giveTagged(NodeTypeResolverInterface::class); } private function registerNodeNameResolvers(RectorConfig $rectorConfig): void { // node name resolvers - $rectorConfig->when(NodeNameResolver::class) - ->needs('$nodeNameResolvers') - ->giveTagged(NodeNameResolverInterface::class); - - $rectorConfig->when(AttributeGroupNamedArgumentManipulator::class) - ->needs('$converterAttributeDecorators') - ->giveTagged(ConverterAttributeDecoratorInterface::class); - $this->registerTagged( $rectorConfig, self::CONVERTER_ATTRIBUTE_DECORATOR_CLASSES, @@ -575,18 +497,18 @@ private function registerRectorAutowiring(RectorConfig $rectorConfig): void { $rectorConfig->afterResolving( AbstractRector::class, - static function (AbstractRector $rector, Container $container): void { + static function (AbstractRector $rector) use ($rectorConfig): void { $rector->autowire( - $container->get(NodeNameResolver::class), - $container->get(NodeTypeResolver::class), - $container->get(SimpleCallableNodeTraverser::class), - $container->get(NodeFactory::class), - $container->get(Skipper::class), - $container->get(NodeComparator::class), - $container->get(CurrentFileProvider::class), - $container->get(CreatedByRuleDecorator::class), - $container->get(ChangedNodeScopeRefresher::class), - $container->get(CommentsMerger::class), + $rectorConfig->get(NodeNameResolver::class), + $rectorConfig->get(NodeTypeResolver::class), + $rectorConfig->get(SimpleCallableNodeTraverser::class), + $rectorConfig->get(NodeFactory::class), + $rectorConfig->get(Skipper::class), + $rectorConfig->get(NodeComparator::class), + $rectorConfig->get(CurrentFileProvider::class), + $rectorConfig->get(CreatedByRuleDecorator::class), + $rectorConfig->get(ChangedNodeScopeRefresher::class), + $rectorConfig->get(CommentsMerger::class), ); } ); @@ -630,23 +552,14 @@ private function registerTaggedServices(RectorConfig $rectorConfig): void ClassNameImportSkipVoterInterface::class ); - $rectorConfig->alias(SymfonyStyle::class, RectorStyle::class); - $rectorConfig->singleton( SymfonyStyle::class, - static function (Container $container): SymfonyStyle { - $symfonyStyleFactory = $container->make(SymfonyStyleFactory::class); + static function (RectorConfig $rectorConfig): SymfonyStyle { + $symfonyStyleFactory = $rectorConfig->make(SymfonyStyleFactory::class); return $symfonyStyleFactory->create(); } ); - $rectorConfig->when(AnnotationToAttributeMapper::class) - ->needs('$annotationToAttributeMappers') - ->giveTagged(AnnotationToAttributeMapperInterface::class); - - $rectorConfig->when(OutputFormatterCollector::class) - ->needs('$outputFormatters') - ->giveTagged(OutputFormatterInterface::class); } private function registerAnnotationToAttributeSetters(RectorConfig $rectorConfig): void @@ -655,10 +568,9 @@ private function registerAnnotationToAttributeSetters(RectorConfig $rectorConfig $rectorConfig->afterResolving( ArrayAnnotationToAttributeMapper::class, static function ( - ArrayAnnotationToAttributeMapper $arrayAnnotationToAttributeMapper, - Container $container - ): void { - $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + ArrayAnnotationToAttributeMapper $arrayAnnotationToAttributeMapper + ) use ($rectorConfig): void { + $annotationToAttributeMapper = $rectorConfig->make(AnnotationToAttributeMapper::class); $arrayAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); } ); @@ -666,20 +578,19 @@ static function ( $rectorConfig->afterResolving( ArrayItemNodeAnnotationToAttributeMapper::class, static function ( - ArrayItemNodeAnnotationToAttributeMapper $arrayItemNodeAnnotationToAttributeMapper, - Container $container - ): void { - $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + ArrayItemNodeAnnotationToAttributeMapper $arrayItemNodeAnnotationToAttributeMapper + ) use ($rectorConfig): void { + $annotationToAttributeMapper = $rectorConfig->make(AnnotationToAttributeMapper::class); $arrayItemNodeAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); } ); $rectorConfig->afterResolving( PlainValueParser::class, - static function (PlainValueParser $plainValueParser, Container $container): void { + static function (PlainValueParser $plainValueParser) use ($rectorConfig): void { $plainValueParser->autowire( - $container->make(StaticDoctrineAnnotationParser::class), - $container->make(ArrayParser::class), + $rectorConfig->make(StaticDoctrineAnnotationParser::class), + $rectorConfig->make(ArrayParser::class), ); } ); @@ -687,10 +598,9 @@ static function (PlainValueParser $plainValueParser, Container $container): void $rectorConfig->afterResolving( CurlyListNodeAnnotationToAttributeMapper::class, static function ( - CurlyListNodeAnnotationToAttributeMapper $curlyListNodeAnnotationToAttributeMapper, - Container $container - ): void { - $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + CurlyListNodeAnnotationToAttributeMapper $curlyListNodeAnnotationToAttributeMapper + ) use ($rectorConfig): void { + $annotationToAttributeMapper = $rectorConfig->make(AnnotationToAttributeMapper::class); $curlyListNodeAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); } ); @@ -698,10 +608,9 @@ static function ( $rectorConfig->afterResolving( DoctrineAnnotationAnnotationToAttributeMapper::class, static function ( - DoctrineAnnotationAnnotationToAttributeMapper $doctrineAnnotationAnnotationToAttributeMapper, - Container $container - ): void { - $annotationToAttributeMapper = $container->make(AnnotationToAttributeMapper::class); + DoctrineAnnotationAnnotationToAttributeMapper $doctrineAnnotationAnnotationToAttributeMapper + ) use ($rectorConfig): void { + $annotationToAttributeMapper = $rectorConfig->make(AnnotationToAttributeMapper::class); $doctrineAnnotationAnnotationToAttributeMapper->autowire($annotationToAttributeMapper); } ); @@ -709,10 +618,6 @@ static function ( private function registerNodeVisitorsAndPhpDoc(RectorConfig $rectorConfig): void { - $rectorConfig->when(PHPStanNodeScopeResolver::class) - ->needs('$decoratingNodeVisitors') - ->giveTagged(DecoratingNodeVisitorInterface::class); - $this->registerTagged( $rectorConfig, self::DECORATING_NODE_VISITOR_CLASSES, @@ -721,14 +626,10 @@ private function registerNodeVisitorsAndPhpDoc(RectorConfig $rectorConfig): void $this->createPHPStanServices($rectorConfig); - $rectorConfig->when(PhpDocNodeMapper::class) - ->needs('$phpDocNodeVisitors') - ->giveTagged(BasePhpDocNodeVisitorInterface::class); - // phpdoc-parser $rectorConfig->singleton( ParserConfig::class, - static fn (Container $container): ParserConfig => new ParserConfig([ + static fn (RectorConfig $rectorConfig): ParserConfig => new ParserConfig([ 'lines' => true, 'indexes' => true, 'comments' => true, @@ -740,33 +641,32 @@ private function registerNodeVisitorsAndPhpDoc(RectorConfig $rectorConfig): void * @param array $classes * @param class-string $tagInterface */ - private function registerTagged(Container $container, array $classes, string $tagInterface): void + private function registerTagged(RectorConfig $rectorConfig, array $classes, string $tagInterface): void { foreach ($classes as $class) { Assert::isAOf($class, $tagInterface); - $container->singleton($class); - $container->tag($class, $tagInterface); + $rectorConfig->singleton($class); } } private function createPHPStanServices(RectorConfig $rectorConfig): void { - $rectorConfig->singleton(Parser::class, static function (Container $container) { - $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + $rectorConfig->singleton(Parser::class, static function (RectorConfig $rectorConfig) { + $phpStanServicesFactory = $rectorConfig->make(PHPStanServicesFactory::class); return $phpStanServicesFactory->createPHPStanParser(); }); - $rectorConfig->singleton(Lexer::class, static function (Container $container) { - $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + $rectorConfig->singleton(Lexer::class, static function (RectorConfig $rectorConfig) { + $phpStanServicesFactory = $rectorConfig->make(PHPStanServicesFactory::class); return $phpStanServicesFactory->createEmulativeLexer(); }); foreach (self::PUBLIC_PHPSTAN_SERVICE_TYPES as $publicPhpstanServiceType) { - $rectorConfig->singleton($publicPhpstanServiceType, static function (Container $container) use ( + $rectorConfig->singleton($publicPhpstanServiceType, static function (RectorConfig $rectorConfig) use ( $publicPhpstanServiceType ) { - $phpStanServicesFactory = $container->make(PHPStanServicesFactory::class); + $phpStanServicesFactory = $rectorConfig->make(PHPStanServicesFactory::class); return $phpStanServicesFactory->getByType($publicPhpstanServiceType); }); } diff --git a/src/NodeNameResolver/NodeNameResolver.php b/src/NodeNameResolver/NodeNameResolver.php index 11cf3b5a490..90e5158b20e 100644 --- a/src/NodeNameResolver/NodeNameResolver.php +++ b/src/NodeNameResolver/NodeNameResolver.php @@ -47,7 +47,7 @@ final class NodeNameResolver public function __construct( private readonly ClassNaming $classNaming, private readonly CallAnalyzer $callAnalyzer, - private readonly iterable $nodeNameResolvers = [] + private readonly array $nodeNameResolvers ) { } diff --git a/src/NodeTypeResolver/NodeTypeResolver.php b/src/NodeTypeResolver/NodeTypeResolver.php index bbbddbe1164..a516fe7a2b9 100644 --- a/src/NodeTypeResolver/NodeTypeResolver.php +++ b/src/NodeTypeResolver/NodeTypeResolver.php @@ -79,7 +79,7 @@ public function __construct( private readonly RenamedClassesDataCollector $renamedClassesDataCollector, private readonly NodeNameResolver $nodeNameResolver, private readonly PhpVersionProvider $phpVersionProvider, - iterable $nodeTypeResolvers + array $nodeTypeResolvers ) { foreach ($nodeTypeResolvers as $nodeTypeResolver) { if ($nodeTypeResolver instanceof NodeTypeResolverAwareInterface) { diff --git a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php index 7c73de9d1ab..97a09faf9a3 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php +++ b/src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php @@ -126,7 +126,7 @@ public function __construct( private NodeScopeResolver $nodeScopeResolver, private ReflectionProvider $reflectionProvider, - iterable $decoratingNodeVisitors, + array $decoratingNodeVisitors, private ScopeFactory $scopeFactory, private PrivatesAccessor $privatesAccessor, private NodeNameResolver $nodeNameResolver, diff --git a/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php b/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php index 2a244aa6086..f22b34eaad2 100644 --- a/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php +++ b/src/StaticTypeMapper/Mapper/PhpParserNodeMapper.php @@ -15,7 +15,7 @@ * @param PhpParserNodeMapperInterface[] $phpParserNodeMappers */ public function __construct( - private iterable $phpParserNodeMappers + private array $phpParserNodeMappers ) { } diff --git a/src/Testing/PHPUnit/AbstractRectorTestCase.php b/src/Testing/PHPUnit/AbstractRectorTestCase.php index dc585fa10bf..f90461c745a 100644 --- a/src/Testing/PHPUnit/AbstractRectorTestCase.php +++ b/src/Testing/PHPUnit/AbstractRectorTestCase.php @@ -4,7 +4,6 @@ namespace Rector\Testing\PHPUnit; -use Illuminate\Container\RewindableGenerator; use Iterator; use Nette\Utils\FileSystem; use Nette\Utils\Strings; @@ -18,18 +17,15 @@ use Rector\Configuration\Parameter\SimpleParameterProvider; use Rector\Contract\DependencyInjection\ResettableInterface; use Rector\Contract\Rector\RectorInterface; -use Rector\DependencyInjection\Laravel\ContainerMemento; use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\DependencyInjection\PHPStanServicesFactory; use Rector\NodeTypeResolver\Reflection\BetterReflection\SourceLocatorProvider\DynamicSourceLocatorProvider; use Rector\PhpParser\NodeTraverser\RectorNodeTraverser; -use Rector\Rector\AbstractRector; use Rector\Testing\Contract\RectorTestInterface; use Rector\Testing\Fixture\FixtureFileFinder; use Rector\Testing\Fixture\FixtureFileUpdater; use Rector\Testing\Fixture\FixtureSplitter; use Rector\Testing\PHPUnit\ValueObject\RectorTestResult; -use Rector\Util\Reflection\PrivatesAccessor; use Rector\ValueObject\PhpVersion; /** @@ -94,8 +90,7 @@ protected function setUp(): void ->changeComposerJsonFilePath($this->provideComposerJsonFilePath()); // reset - /** @var RewindableGenerator $resettables */ - $resettables = $rectorConfig->tagged(ResettableInterface::class); + $resettables = $rectorConfig->findByContract(ResettableInterface::class); foreach ($resettables as $resettable) { /** @var ResettableInterface $resettable */ @@ -106,15 +101,12 @@ protected function setUp(): void $rectorConfig->resetRuleConfigurations(); // this has to be always empty, so we can add new rules with their configuration - $this->assertEmpty($rectorConfig->tagged(RectorInterface::class)); + $this->assertEmpty($rectorConfig->findByContract(RectorInterface::class)); $this->bootFromConfigFiles([$configFile]); - $rectorsGenerator = $rectorConfig->tagged(RectorInterface::class); - $rectors = $rectorsGenerator instanceof RewindableGenerator - ? iterator_to_array($rectorsGenerator->getIterator()) - // no rules at all, e.g. in case of only post rector run - : []; + // no rules at all, e.g. in case of only post rector run, yields an empty array + $rectors = $rectorConfig->findByContract(RectorInterface::class); /** @var RectorNodeTraverser $rectorNodeTraverser */ $rectorNodeTraverser = $rectorConfig->make(RectorNodeTraverser::class); @@ -217,30 +209,8 @@ protected function doTestFileExpectingWarningAboutRuleApplied( private function forgetRectorsRules(): void { - $rectorConfig = self::getContainer(); - - // 1. forget tagged services - ContainerMemento::forgetTag($rectorConfig, RectorInterface::class); - - // 2. remove after binding too, to avoid setting configuration over and over again - $privatesAccessor = new PrivatesAccessor(); - $privatesAccessor->propertyClosure( - $rectorConfig, - 'afterResolvingCallbacks', - static function (array $afterResolvingCallbacks): array { - foreach (array_keys($afterResolvingCallbacks) as $key) { - if ($key === AbstractRector::class) { - continue; - } - - if (is_a($key, RectorInterface::class, true)) { - unset($afterResolvingCallbacks[$key]); - } - } - - return $afterResolvingCallbacks; - } - ); + // forget the rules and their per-rule configuration callbacks, so a re-boot starts clean + self::getContainer()->forgetByContract(RectorInterface::class); } private function doTestFileMatchesExpectedContent( diff --git a/src/Util/Reflection/PrivatesAccessor.php b/src/Util/Reflection/PrivatesAccessor.php index 3d7512277ce..442810bd563 100644 --- a/src/Util/Reflection/PrivatesAccessor.php +++ b/src/Util/Reflection/PrivatesAccessor.php @@ -14,19 +14,6 @@ */ final class PrivatesAccessor { - /** - * @param callable(mixed $value): mixed $closure - */ - public function propertyClosure(object $object, string $propertyName, callable $closure): void - { - $property = $this->getPrivateProperty($object, $propertyName); - - // modify value - $property = $closure($property); - - $this->setPrivateProperty($object, $propertyName, $property); - } - /** * @param object|class-string $object * @param mixed[] $arguments diff --git a/src/functions/node_helper.php b/src/functions/node_helper.php index ac4ac61782f..d0d69a2ca36 100644 --- a/src/functions/node_helper.php +++ b/src/functions/node_helper.php @@ -2,12 +2,12 @@ declare(strict_types=1); -use Illuminate\Container\Container; use PhpParser\Node; use PhpParser\PrettyPrinter\Standard; use Rector\Console\Style\SymfonyStyleFactory; use Rector\PhpParser\Node\FileNode; use Rector\Util\NodePrinter; +use Rector\Util\Reflection\PrivatesAccessor; use Symfony\Component\Console\Output\OutputInterface; if (! function_exists('print_node')) { @@ -36,9 +36,8 @@ function print_node(Node|array $node): void */ function dump_node(Node|array $node): void { - $rectorStyle = Container::getInstance() - ->make(SymfonyStyleFactory::class) - ->create(); + $symfonyStyleFactory = new SymfonyStyleFactory(new PrivatesAccessor()); + $rectorStyle = $symfonyStyleFactory->create(); // we turn up the verbosity so it's visible in tests overriding the // default which is to be quite during tests diff --git a/tests/Bin/RectorTest.php b/tests/Bin/RectorTest.php index bd9e85de090..fcb3d619a3a 100644 --- a/tests/Bin/RectorTest.php +++ b/tests/Bin/RectorTest.php @@ -22,11 +22,11 @@ public static function outputProvider(): Iterator ]; yield 'Exception with previous console output' => [ 'command' => PHP_BINARY . ' bin/rector -c tests/Bin/config/incorrect-phpstan-files.php', - 'expectedOutput' => PHP_EOL . ' [ERROR] Rector\\NodeTypeResolver\\DependencyInjection\\PHPStanServicesFactory ' . PHP_EOL . PHP_EOL . " [ERROR] Unexpected item 'parameters › invalidParameters'. " . PHP_EOL . PHP_EOL, + 'expectedOutput' => PHP_EOL . " [ERROR] Unexpected item 'parameters › invalidParameters'. " . PHP_EOL . PHP_EOL, ]; yield 'Exception with previous console output in JSON format' => [ 'command' => PHP_BINARY . ' bin/rector -c tests/Bin/config/incorrect-phpstan-files.php --output-format json', - 'expectedOutput' => '{"fatal_errors":["Rector\\\\NodeTypeResolver\\\\DependencyInjection\\\\PHPStanServicesFactory","Unexpected item \'parameters › invalidParameters\'."]}', + 'expectedOutput' => '{"fatal_errors":["Unexpected item \'parameters › invalidParameters\'."]}', ]; } diff --git a/tests/Configuration/OnlyRuleResolverTest.php b/tests/Configuration/OnlyRuleResolverTest.php index 3406daeef56..6c6e0da273c 100644 --- a/tests/Configuration/OnlyRuleResolverTest.php +++ b/tests/Configuration/OnlyRuleResolverTest.php @@ -25,7 +25,7 @@ protected function setUp(): void $rectorConfig = self::getContainer(); $this->onlyRuleResolver = new OnlyRuleResolver( - iterator_to_array($rectorConfig->tagged(RectorInterface::class)), + $rectorConfig->findByContract(RectorInterface::class), ); } diff --git a/tests/DependencyInjection/TaggedServicesTest.php b/tests/DependencyInjection/TaggedServicesTest.php index 26ee6f0b840..b8654a5d3ef 100644 --- a/tests/DependencyInjection/TaggedServicesTest.php +++ b/tests/DependencyInjection/TaggedServicesTest.php @@ -21,7 +21,7 @@ final class TaggedServicesTest extends AbstractLazyTestCase #[DataProvider('provideTagInterfaces')] public function testServiceIsTaggedOnce(string $tagInterface): void { - $taggedServices = iterator_to_array(self::getContainer()->tagged($tagInterface)); + $taggedServices = self::getContainer()->findByContract($tagInterface); $classNames = array_map(static fn (object $service): string => $service::class, $taggedServices); diff --git a/tests/Issues/Issue9388/config/configured_rule.php b/tests/Issues/Issue9388/config/configured_rule.php index 9f59172b0bd..45aa0ee0743 100644 --- a/tests/Issues/Issue9388/config/configured_rule.php +++ b/tests/Issues/Issue9388/config/configured_rule.php @@ -4,18 +4,15 @@ use Rector\Config\RectorConfig; use Rector\Renaming\Rector\Name\RenameClassRector; -use Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute\AttributeDecorator; -use Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute\AttributeDecoratorInterface; use Rector\Tests\Issues\Issue9388\Source\AnnotationToAttribute\ValidateAttributeDecorator; use Rector\Tests\Issues\Issue9388\Source\Rule\ExtbaseAnnotationToAttributeRector; use Rector\ValueObject\PhpVersionFeature; return static function (RectorConfig $rectorConfig): void { - $rectorConfig->autotagInterface(AttributeDecoratorInterface::class); + // AttributeDecorator receives every AttributeDecoratorInterface implementation through its + // "@param AttributeDecoratorInterface[] $decorators" docblock; registering the implementation + // is enough for the container to discover and inject it by contract $rectorConfig->singleton(ValidateAttributeDecorator::class); - $rectorConfig->when(AttributeDecorator::class)->needs('$decorators')->giveTagged( - AttributeDecoratorInterface::class - ); $rectorConfig->importNames(false, false); $rectorConfig->phpVersion(PhpVersionFeature::ATTRIBUTES); diff --git a/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php b/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php index 9d554611350..2010b2ae5cc 100644 --- a/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php +++ b/tests/PHPStanStaticTypeMapper/TypeMapperOrderTest.php @@ -42,10 +42,10 @@ public function testChildTypeMapperIsRegisteredBeforeItsParent(): void */ private function resolveTypeMappers(): array { - $typeMappers = iterator_to_array(self::getContainer()->tagged(TypeMapperInterface::class)); + $typeMappers = self::getContainer()->findByContract(TypeMapperInterface::class); $this->assertNotEmpty($typeMappers); - return array_values($typeMappers); + return $typeMappers; } } diff --git a/tests/Skipper/Skipper/SkipperRectorRuleTest.php b/tests/Skipper/Skipper/SkipperRectorRuleTest.php index 414634e9ed3..424f2b44ade 100644 --- a/tests/Skipper/Skipper/SkipperRectorRuleTest.php +++ b/tests/Skipper/Skipper/SkipperRectorRuleTest.php @@ -4,7 +4,6 @@ namespace Rector\Tests\Skipper\Skipper; -use Illuminate\Container\RewindableGenerator; use PHPStan\Reflection\BetterReflection\SourceLocator\FileNodesFetcher; use Rector\Configuration\Option; use Rector\Configuration\Parameter\SimpleParameterProvider; @@ -39,11 +38,9 @@ public function testRemovingServiceFromContainer(): void $rectorConfig->make(FileNodesFetcher::class); // here 1 rule should be removed and 1 should remain - /** @var RewindableGenerator $rectorsIterator */ - $rectorsIterator = $rectorConfig->tagged(RectorInterface::class); - $this->assertCount(1, $rectorsIterator); + $rectors = $rectorConfig->findByContract(RectorInterface::class); + $this->assertCount(1, $rectors); - $rectors = iterator_to_array($rectorsIterator->getIterator()); $this->assertInstanceOf(RemoveUnusedPromotedPropertyRector::class, $rectors[0]); } }