diff --git a/composer.json b/composer.json index 7e2b22f9573..0141cc09fc0 100644 --- a/composer.json +++ b/composer.json @@ -49,6 +49,7 @@ "symplify/skipper": "^10.0", "symplify/smart-file-system": "^10.0", "symplify/symfony-php-config": "^10.0", + "symplify/vendor-patches": "^10.0", "tracy/tracy": "^2.8", "webmozart/assert": "^1.10" }, @@ -142,6 +143,11 @@ "release": "vendor/bin/monorepo-builder release patch --ansi" }, "extra": { + "patches": { + "symfony/dependency-injection": [ + "patches/symfony-dependency-injection-loader-configurator-servicesconfigurator-php.patch" + ] + }, "branch-alias": { "dev-main": "0.12-dev" } diff --git a/patches/symfony-dependency-injection-loader-configurator-servicesconfigurator-php.patch b/patches/symfony-dependency-injection-loader-configurator-servicesconfigurator-php.patch new file mode 100644 index 00000000000..0fd389c5138 --- /dev/null +++ b/patches/symfony-dependency-injection-loader-configurator-servicesconfigurator-php.patch @@ -0,0 +1,20 @@ +--- /dev/null ++++ ../Loader/Configurator/ServicesConfigurator.php +@@ -70,7 +70,7 @@ + * @param string|null $id The service id, or null to create an anonymous service + * @param string|null $class The class of the service, or null when $id is also the class name + */ +- final public function set(?string $id, string $class = null): ServiceConfigurator ++ final public function set(?string $id, string $class = null): \Rector\Core\DependencyInjection\Loader\Configurator\RectorServiceConfigurator + { + $defaults = $this->defaults; + $definition = new Definition(); +@@ -91,7 +91,7 @@ + $definition->setBindings(unserialize(serialize($defaults->getBindings()))); + $definition->setChanges([]); + +- $configurator = new ServiceConfigurator($this->container, $this->instanceof, true, $this, $definition, $id, $defaults->getTags(), $this->path); ++ $configurator = new \Rector\Core\DependencyInjection\Loader\Configurator\RectorServiceConfigurator($this->container, $this->instanceof, true, $this, $definition, $id, $defaults->getTags(), $this->path); + + return null !== $class ? $configurator->class($class) : $configurator; + } diff --git a/phpstan.neon b/phpstan.neon index 3c9125a9c6e..e5dae16c0c6 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -514,3 +514,11 @@ parameters: paths: - packages/StaticTypeMapper/PhpDocParser/IntersectionTypeMapper.php - packages/StaticTypeMapper/PhpParser/IntersectionTypeNodeMapper.php + + - + message: '#Use separate function calls with readable variable names#' + path: src/DependencyInjection/Loader/Configurator/RectorServiceConfigurator.php + + - + message: '#\$service\-\>call\("configure", \[\[ \.\.\. \]\]\) must be followed by exact array#' + path: src/DependencyInjection/Loader/Configurator/RectorServiceConfigurator.php diff --git a/rector.php b/rector.php index 6148f955625..4839166492b 100644 --- a/rector.php +++ b/rector.php @@ -15,7 +15,6 @@ use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; -use Symplify\SymfonyPhpConfig\ValueObjectInliner; return static function (ContainerConfigurator $containerConfigurator): void { // include the latest PHP version + all bellow in one config! @@ -38,18 +37,18 @@ // phpunit $services->set(PreferThisOrSelfMethodCallRector::class) - ->call('configure', [[ + ->configure([ PreferThisOrSelfMethodCallRector::TYPE_TO_PREFERENCE => [ - TestCase::class => ValueObjectInliner::inline(PreferenceSelfThis::PREFER_THIS()), + TestCase::class => PreferenceSelfThis::PREFER_THIS(), ], - ]]); + ]); $services->set(ReturnArrayClassMethodToYieldRector::class) - ->call('configure', [[ - ReturnArrayClassMethodToYieldRector::METHODS_TO_YIELDS => ValueObjectInliner::inline([ + ->configure([ + ReturnArrayClassMethodToYieldRector::METHODS_TO_YIELDS => [ new ReturnArrayClassMethodToYield('PHPUnit\Framework\TestCase', '*provide*'), - ]), - ]]); + ], + ]); $parameters = $containerConfigurator->parameters(); diff --git a/src/DependencyInjection/Loader/Configurator/RectorServiceConfigurator.php b/src/DependencyInjection/Loader/Configurator/RectorServiceConfigurator.php new file mode 100644 index 00000000000..5225beb38bc --- /dev/null +++ b/src/DependencyInjection/Loader/Configurator/RectorServiceConfigurator.php @@ -0,0 +1,54 @@ + $configuration + */ + public function configure(array $configuration): self + { + $this->ensureClassIsConfigurable($this->id); + + // decorate with value object inliner so Symfony understands, see https://getrector.org/blog/2020/09/07/how-to-inline-value-object-in-symfony-php-config + array_walk_recursive($configuration, function (&$value) { + if (is_object($value)) { + $value = ValueObjectInliner::inline($value); + } + + return $value; + }); + + $this->call('configure', [$configuration]); + + return $this; + } + + private function ensureClassIsConfigurable(?string $class): void + { + if ($class === null) { + throw new InvalidConfigurationException('The class is missing'); + } + + if (! is_a($class, ConfigurableRectorInterface::class, true)) { + $errorMessage = sprintf( + 'The service "%s" is not configurable. Make it implement "%s" or remove "configure()" call.', + $class, + ConfigurableRectorInterface::class, + ); + throw new InvalidConfigurationException($errorMessage); + } + } +}