diff --git a/config/drupal-10/drupal-10.2-deprecations.php b/config/drupal-10/drupal-10.2-deprecations.php index 869a4838..6e343b9a 100644 --- a/config/drupal-10/drupal-10.2-deprecations.php +++ b/config/drupal-10/drupal-10.2-deprecations.php @@ -2,6 +2,8 @@ declare(strict_types=1); +use DrupalRector\Drupal10\Rector\Deprecation\VersionedFunctionToServiceRector; +use DrupalRector\Drupal10\Rector\ValueObject\VersionedFunctionToServiceConfiguration; use DrupalRector\Rector\Deprecation\FunctionToStaticRector; use DrupalRector\Rector\Deprecation\MethodToMethodWithCheckRector; use DrupalRector\Rector\ValueObject\FunctionToStaticConfiguration; @@ -24,4 +26,9 @@ new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'getResource', 'getImage'), new MethodToMethodWithCheckConfiguration('Drupal\system\Plugin\ImageToolkit\GDToolkit', 'setResource', 'setImage'), ]); + + // https://www.drupal.org/node/3358337 + $rectorConfig->ruleWithConfiguration(VersionedFunctionToServiceRector::class, [ + new VersionedFunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), + ]); }; diff --git a/src/Drupal10/Rector/Deprecation/RenameClassRector.php b/src/Drupal10/Rector/Deprecation/RenameClassRector.php new file mode 100644 index 00000000..cb43f9e3 --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/RenameClassRector.php @@ -0,0 +1,109 @@ +renamedClassesDataCollector = $renamedClassesDataCollector; + $this->classRenamer = $classRenamer; + } + + /** + * {@inheritdoc} + */ + public function getNodeTypes(): array + { + return [FullyQualified::class, Property::class, FunctionLike::class, Expression::class, ClassLike::class, If_::class]; + } + + public function configure(array $configuration): void + { + foreach ($configuration as $value) { + if (!($value instanceof RenameClassRectorConfiguration)) { + throw new \InvalidArgumentException(sprintf('Each configuration item must be an instance of "%s"', RenameClassRectorConfiguration::class)); + } + } + + parent::configure($configuration); + } + + public function refactorWithConfiguration(Node $node, VersionedConfigurationInterface $configuration) + { + $oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses(); + if ($oldToNewClasses !== []) { + /** @var \PHPStan\Analyser\Scope $scope */ + $scope = $node->getAttribute(AttributeKey::SCOPE); + $return = $this->classRenamer->renameNode($node, $oldToNewClasses, $scope); + if (!is_null($return)) { + $scope->getFile(); + + return $return; + } + } + + return null; + } + + /** + * {@inheritdoc} + */ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Fixes deprecated watchdog_exception(\'update\', $exception) calls', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +watchdog_exception('update', $exception); +CODE_BEFORE + , + <<<'CODE_AFTER' +use \Drupal\Core\Utility\Error; +$logger = \Drupal::logger('update'); +Error::logException($logger, $exception); +CODE_AFTER + , + [ + new DrupalIntroducedVersionConfiguration('10.1.0'), + ] + ), + ]); + } +} diff --git a/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php b/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php new file mode 100644 index 00000000..4b166826 --- /dev/null +++ b/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector.php @@ -0,0 +1,84 @@ +getName($node->name) === $configuration->getDeprecatedFunctionName()) { + // This creates a service call like `\Drupal::service('file_system'). + $service = new Node\Expr\StaticCall(new Node\Name\FullyQualified('Drupal'), 'service', [new Node\Arg(new Node\Scalar\String_($configuration->getServiceName()))]); + + $method_name = new Node\Identifier($configuration->getServiceMethodName()); + + return new Node\Expr\MethodCall($service, $method_name, $node->args); + } + + return null; + } + + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Fixes deprecated function to service calls, used in Drupal 8 and 9 deprecations', [ + new ConfiguredCodeSample( + <<<'CODE_BEFORE' +_drupal_flush_css_js(); +CODE_BEFORE + , + <<<'CODE_AFTER' +\Drupal::service('asset.query_string')->reset(); +CODE_AFTER + , + [ + new VersionedFunctionToServiceConfiguration('10.2.0', '_drupal_flush_css_js', 'asset.query_string', 'reset'), + ] + ), + ]); + } +} diff --git a/src/Drupal10/Rector/ValueObject/RenameClassRectorConfiguration.php b/src/Drupal10/Rector/ValueObject/RenameClassRectorConfiguration.php new file mode 100644 index 00000000..65b6641c --- /dev/null +++ b/src/Drupal10/Rector/ValueObject/RenameClassRectorConfiguration.php @@ -0,0 +1,38 @@ +introducedVersion = $introducedVersion; + $this->oldClass = $oldClass; + $this->newClass = $newClass; + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } + + public function getOldClass(): string + { + return $this->oldClass; + } + + public function getNewClass(): string + { + return $this->newClass; + } +} diff --git a/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php b/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php new file mode 100644 index 00000000..e0068162 --- /dev/null +++ b/src/Drupal10/Rector/ValueObject/VersionedFunctionToServiceConfiguration.php @@ -0,0 +1,55 @@ +deprecatedFunctionName = $deprecatedFunctionName; + $this->serviceName = $serviceName; + $this->serviceMethodName = $serviceMethodName; + $this->introducedVersion = $introducedVersion; + } + + public function getDeprecatedFunctionName(): string + { + return $this->deprecatedFunctionName; + } + + public function getServiceName(): string + { + return $this->serviceName; + } + + public function getServiceMethodName(): string + { + return $this->serviceMethodName; + } + + public function getIntroducedVersion(): string + { + return $this->introducedVersion; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/RenameClassRectorTest.php b/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/RenameClassRectorTest.php new file mode 100644 index 00000000..9d76f4fc --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/RenameClassRectorTest.php @@ -0,0 +1,35 @@ +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + // must be implemented + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/config/configured_rule.php new file mode 100644 index 00000000..f8014b1a --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/RenameClassRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + +----- +doTestFile($filePath); + } + + /** + * @return Iterator<> + */ + public static function provideData(): \Iterator + { + return self::yieldFilesFromDirectory(__DIR__.'/fixture'); + } + + public function provideConfigFilePath(): string + { + // must be implemented + return __DIR__.'/config/configured_rule.php'; + } +} diff --git a/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php b/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php new file mode 100644 index 00000000..24b63efd --- /dev/null +++ b/tests/src/Drupal10/Rector/Deprecation/VersionedFunctionToServiceRector/config/configured_rule.php @@ -0,0 +1,14 @@ + +----- + \Drupal::service('asset.query_string')->reset(), fn() => _drupal_flush_css_js()); +} +?>