diff --git a/src/Configuration/Typo3Option.php b/src/Configuration/Typo3Option.php new file mode 100644 index 000000000..54fcd01f2 --- /dev/null +++ b/src/Configuration/Typo3Option.php @@ -0,0 +1,13 @@ +parameterProvider = $parameterProvider; + $this->nameImporter = $nameImporter; + $this->importDocBlocks = (bool) $parameterProvider->provideParameter(Option::IMPORT_DOC_BLOCKS); + $this->docBlockNameImporter = $docBlockNameImporter; + $this->currentFileInfoProvider = $currentFileInfoProvider; + } + + public function enterNode(Node $node): ?Node + { + $autoImportNames = (bool) $this->parameterProvider->provideParameter(Typo3Option::AUTO_IMPORT_NAMES); + if (! $autoImportNames) { + return null; + } + + if ($this->shouldSkip($this)) { + return null; + } + + if ($node instanceof Name) { + return $this->nameImporter->importName($node); + } + + if (! $this->importDocBlocks) { + return null; + } + + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if (null === $phpDocInfo) { + return null; + } + + $hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node); + if (! $hasChanged) { + return null; + } + + return $node; + } + + public function getPriority(): int + { + // The \Rector\PostRector\Rector\NameImportingPostRector::class from Rector itself uses 600, so we go one level up + return 601; + } + + public function getDefinition(): RectorDefinition + { + return new RectorDefinition( + 'Imports fully qualified class names in parameter types, return types, extended classes, implemented, interfaces and even docblocks' + ); + } + + private function shouldSkip(PostRectorInterface $postRector): bool + { + $smartFileInfo = $this->currentFileInfoProvider->getSmartFileInfo(); + + if (! $smartFileInfo instanceof SmartFileInfo) { + return false; + } + + $skip = $this->parameterProvider->provideArrayParameter(Option::SKIP); + if ([] === $skip) { + return false; + } + + $rectorClass = get_class($postRector); + if (! array_key_exists($rectorClass, $skip)) { + return false; + } + + $locations = $skip[$rectorClass]; + $filePathName = $smartFileInfo->getPathName(); + if (in_array($filePathName, $locations, true)) { + return true; + } + + $fileName = $smartFileInfo->getFileName(); + foreach ($locations as $location) { + $ignoredPath = $this->normalizeForFnmatch($location); + + if ($smartFileInfo->endsWith($ignoredPath) || $smartFileInfo->doesFnmatch($ignoredPath)) { + return true; + } + + if (rtrim($ignoredPath, '\/') . DIRECTORY_SEPARATOR . $fileName === $filePathName) { + return true; + } + } + + return false; + } + + /** + * "value*" → "*value*" + * "*value" → "*value*" + */ + private function normalizeForFnmatch(string $path): string + { + // ends with * + if (Strings::match($path, self::ONLY_ENDS_WITH_ASTERISK_REGEX)) { + return '*' . $path; + } + + // starts with * + if (Strings::match($path, self::ONLY_STARTS_WITH_ASTERISK_REGEX)) { + return $path . '*'; + } + + return $path; + } +} diff --git a/templates/rector.php.dist b/templates/rector.php.dist index 3fc770ebc..07549ccc7 100644 --- a/templates/rector.php.dist +++ b/templates/rector.php.dist @@ -5,6 +5,8 @@ declare(strict_types=1); use Rector\Core\Configuration\Option; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; use Ssch\TYPO3Rector\Set\Typo3SetList; +use Ssch\TYPO3Rector\Configuration\Typo3Option; +use Ssch\TYPO3Rector\PostRector\NameImportingPostRector; use Ssch\TYPO3Rector\Rector\v9\v0\InjectAnnotationRector; return static function (ContainerConfigurator $containerConfigurator): void { @@ -17,7 +19,7 @@ return static function (ContainerConfigurator $containerConfigurator): void { ]); // FQN classes are not imported by default. If you don't do it manually after every Rector run, enable it by: - $parameters->set(Option::AUTO_IMPORT_NAMES, true); + $parameters->set(Typo3Option::AUTO_IMPORT_NAMES, true); // this will not import root namespace classes, like \DateTime or \Exception $parameters->set(Option::IMPORT_SHORT_CLASSES, false); @@ -28,21 +30,23 @@ return static function (ContainerConfigurator $containerConfigurator): void { // Define your target version which you want to support $parameters->set(Option::PHP_VERSION_FEATURES, '7.2'); - // If you set option Option::AUTO_IMPORT_NAMES to true, you should consider excluding some TYPO3 files. - $parameters->set(Option::EXCLUDE_PATHS, [ - 'ClassAliasMap.php', - 'class.ext_update.php', - 'ext_localconf.php', - 'ext_emconf.php', - 'ext_tables.php', - __DIR__ . '/**/TCA/*' + // If you set option Typo3Option::AUTO_IMPORT_NAMES to true, you should consider excluding some TYPO3 files. + $parameters->set(Option::SKIP, [ + NameImportingPostRector::class => [ + 'ClassAliasMap.php', + 'class.ext_update.php', + 'ext_localconf.php', + 'ext_emconf.php', + 'ext_tables.php', + __DIR__ . '/**/TCA/*', + ], ]); // If you have trouble that rector cannot run because some TYPO3 constants are not defined add an additional constants file // Have a look at https://github.com/sabbelasichon/typo3-rector/typo3.constants.php - //$parameters->set(Option::AUTOLOAD_PATHS, [ - // __DIR__ . '/typo3.constants.php' - //]); + $parameters->set(Option::AUTOLOAD_PATHS, [ + __DIR__ . '/typo3.constants.php' + ]); // get services (needed for register a single rule) // $services = $containerConfigurator->services();