From d788c767046d8d0008cc13cd66e2a42d0302ceb4 Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Mon, 4 Apr 2022 23:32:59 +0200 Subject: [PATCH 1/2] TASK: Add Icons.php rector --- .../register-icon-to-icons-file.php | 15 ++ .../v11/v5/RegisterIconToIconFileRector.php | 186 ++++++++++++++++++ .../AddIconsToReturnRector.php | 110 +++++++++++ src/Set/Typo3SetList.php | 5 + .../Expected/Configuration/Icons.php.inc | 2 + .../my_extension/ext_localconf.php.inc | 41 ++++ .../RegisterIconToIconFileRectorTest.php | 44 +++++ .../config/configured_rule.php | 11 ++ 8 files changed, 414 insertions(+) create mode 100644 config/register-icon-to-icons-file/register-icon-to-icons-file.php create mode 100644 src/Rector/v11/v5/RegisterIconToIconFileRector.php create mode 100644 src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php create mode 100644 tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc create mode 100644 tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/my_extension/ext_localconf.php.inc create mode 100644 tests/Rector/v11/v5/RegisterIconToIconFileRector/RegisterIconToIconFileRectorTest.php create mode 100644 tests/Rector/v11/v5/RegisterIconToIconFileRector/config/configured_rule.php diff --git a/config/register-icon-to-icons-file/register-icon-to-icons-file.php b/config/register-icon-to-icons-file/register-icon-to-icons-file.php new file mode 100644 index 000000000..38bee8f4e --- /dev/null +++ b/config/register-icon-to-icons-file/register-icon-to-icons-file.php @@ -0,0 +1,15 @@ +import(__DIR__ . '/../config.php'); + + $services = $containerConfigurator->services(); + $services->set(AddIconsToReturnRector::class); + $services->set(RegisterIconToIconFileRector::class); +}; diff --git a/src/Rector/v11/v5/RegisterIconToIconFileRector.php b/src/Rector/v11/v5/RegisterIconToIconFileRector.php new file mode 100644 index 000000000..3321b6ecf --- /dev/null +++ b/src/Rector/v11/v5/RegisterIconToIconFileRector.php @@ -0,0 +1,186 @@ +> + */ + public function getNodeTypes(): array + { + return [Node\Expr\MethodCall::class]; + } + + /** + * @param Node\Expr\MethodCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType( + $node, + new ObjectType('TYPO3\CMS\Core\Imaging\IconRegistry') + )) { + return null; + } + + if (! $this->nodeNameResolver->isName($node->name, 'registerIcon')) { + return null; + } + + $currentSmartFileInfo = $this->file->getSmartFileInfo(); + + $extEmConfFileInfo = $this->filesFinder->findExtEmConfRelativeFromGivenFileInfo($currentSmartFileInfo); + + if (! $extEmConfFileInfo instanceof SmartFileInfo) { + return null; + } + + $extensionDirectory = dirname($extEmConfFileInfo->getRealPath()); + + $iconsFilePath = sprintf('%s/Configuration/Icons.php', $extensionDirectory); + + $identifier = $this->valueResolver->getValue($node->args[0]->value); + + if (! is_string($identifier)) { + return null; + } + + $options = $this->valueResolver->getValue($node->args[2]->value); + + $iconConfiguration = [ + 'provider' => $node->args[1]->value, + ]; + + if (is_array($options)) { + $iconConfiguration = array_merge($iconConfiguration, $options); + } + + $this->addNewIconToIconsFile($iconsFilePath, $identifier, $iconConfiguration); + + $this->removeNode($node); + + return null; + } + + /** + * @codeCoverageIgnore + */ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Generate or add registerIcon calls to Icons.php file', [new CodeSample( + <<<'CODE_SAMPLE' + +use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider; +use TYPO3\CMS\Core\Imaging\IconRegistry; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +$iconRegistry = GeneralUtility::makeInstance(IconRegistry::class); +$iconRegistry->registerIcon( + 'mybitmapicon', + BitmapIconProvider::class, + [ + 'source' => 'EXT:my_extension/Resources/Public/Icons/mybitmap.png', + ] +); +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider; +use TYPO3\CMS\Core\Imaging\IconRegistry; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +$iconRegistry = GeneralUtility::makeInstance(IconRegistry::class); + +// Add Icons.php file +CODE_SAMPLE + )]); + } + + /** + * @param Stmt[] $stmts + */ + private function decorateNamesToFullyQualified(array $stmts): void + { + // decorate nodes with names first + $nameResolverNodeTraverser = new NodeTraverser(); + $nameResolverNodeTraverser->addVisitor(new NameResolver()); + $nameResolverNodeTraverser->traverse($stmts); + } + + /** + * @param array $iconConfiguration + */ + private function addNewIconToIconsFile( + string $iconsFilePath, + string $iconIdentifier, + array $iconConfiguration + ): void { + $addedFilesWithContent = $this->removedAndAddedFilesCollector->getAddedFilesWithContent(); + + $existingIcons = null; + foreach ($addedFilesWithContent as $addedFileWithContent) { + if ($addedFileWithContent->getFilePath() === $iconsFilePath) { + $existingIcons = $addedFileWithContent->getFileContent(); + } + } + + if (is_string($existingIcons)) { + $nodes = $this->simplePhpParser->parseString($existingIcons); + } else { + $nodes = [new Return_($this->nodeFactory->createArray([]))]; + } + + $this->decorateNamesToFullyQualified($nodes); + + $nodeTraverser = new NodeTraverser(); + $this->addIconsToReturnRector->configure([ + AddIconsToReturnRector::ICON_IDENTIFIER => $iconIdentifier, + AddIconsToReturnRector::ICON_CONFIGURATION => $iconConfiguration, + ]); + $nodeTraverser->addVisitor($this->addIconsToReturnRector); + $nodes = $nodeTraverser->traverse($nodes); + + $changedIconsContent = $this->betterStandardPrinter->prettyPrintFile($nodes); + + $changedIconsContent = Strings::replace($changedIconsContent, self::REMOVE_EMPTY_LINES); + + $this->removedAndAddedFilesCollector->addAddedFile( + new AddedFileWithContent($iconsFilePath, $changedIconsContent) + ); + } +} diff --git a/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php b/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php new file mode 100644 index 000000000..c8b28da66 --- /dev/null +++ b/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php @@ -0,0 +1,110 @@ + + */ + private array $iconConfiguration; + + /** + * @codeCoverageIgnore + */ + public function getRuleDefinition(): RuleDefinition + { + return new RuleDefinition('Add arguments to configure method in Symfony Command', [ + new ConfiguredCodeSample( + <<<'CODE_SAMPLE' +return []; +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +return [ + 'my-icon' => [ + 'provider' => stdClass::class, + 'source' => 'mysvg.svg' + ] +]; +CODE_SAMPLE +, + [ + self::ICON_IDENTIFIER => 'my-icon', + self::ICON_CONFIGURATION => [ + 'provider' => \stdClass::class, + 'source' => 'mysvg.svg', + ], + ] + ), + ]); + } + + public function getNodeTypes(): array + { + return [Return_::class]; + } + + /** + * @param Return_ $node + */ + public function refactor(Node $node): ?Node + { + if (! $node->expr instanceof Array_) { + return null; + } + + $iconArrayItem = new ArrayItem($this->nodeFactory->createArray($this->iconConfiguration), new String_( + $this->iconIdentifier + )); + $node->expr->items[] = $iconArrayItem; + + return $node; + } + + /** + * @param array $configuration + */ + public function configure(array $configuration): void + { + $iconIdentifier = $configuration[self::ICON_IDENTIFIER] ?? ''; + $iconConfiguration = $configuration[self::ICON_CONFIGURATION] ?? []; + + Assert::stringNotEmpty($iconIdentifier); + + Assert::isArray($iconConfiguration); + Assert::keyExists($iconConfiguration, 'provider'); + + $this->iconConfiguration = $iconConfiguration; + $this->iconIdentifier = $iconIdentifier; + } +} diff --git a/src/Set/Typo3SetList.php b/src/Set/Typo3SetList.php index a5620986f..38e833434 100644 --- a/src/Set/Typo3SetList.php +++ b/src/Set/Typo3SetList.php @@ -130,4 +130,9 @@ final class Typo3SetList * @var string */ public const EXTBASE_COMMAND_CONTROLLERS_TO_SYMFONY_COMMANDS = __DIR__ . '/../../config/extbase-command-controllers-to-symfony-commands/extbase_command_controllers_to_symfony_commands.php'; + + /** + * @var string + */ + public const REGISTER_ICONS_TO_ICON = __DIR__ . '/../../config/register-icon-to-icons-file/register-icon-to-icons-file.php'; } diff --git a/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc new file mode 100644 index 000000000..881d04a4c --- /dev/null +++ b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc @@ -0,0 +1,2 @@ + ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mybitmap.png'], 'mysvgicon' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mysvg.svg'], 'proof-of-concept' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\FontawesomeIconProvider::class, 'name' => 'spinner', 'spinning' => true]]; diff --git a/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/my_extension/ext_localconf.php.inc b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/my_extension/ext_localconf.php.inc new file mode 100644 index 000000000..a3f909cab --- /dev/null +++ b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/my_extension/ext_localconf.php.inc @@ -0,0 +1,41 @@ +registerIcon( + 'mybitmapicon', + BitmapIconProvider::class, + [ + 'source' => 'EXT:my_extension/Resources/Public/Icons/mybitmap.png', + ] +); +$iconRegistry->registerIcon( + 'mysvgicon', + SvgIconProvider::class, + [ + 'source' => 'EXT:my_extension/Resources/Public/Icons/mysvg.svg', + ] +); +$iconRegistry->registerIcon( + 'proof-of-concept', + \TYPO3\CMS\Core\Imaging\IconProvider\FontawesomeIconProvider::class, + [ + 'name' => 'spinner', + 'spinning' => true + ] +); +?> +----- + diff --git a/tests/Rector/v11/v5/RegisterIconToIconFileRector/RegisterIconToIconFileRectorTest.php b/tests/Rector/v11/v5/RegisterIconToIconFileRector/RegisterIconToIconFileRectorTest.php new file mode 100644 index 000000000..ea2ad6e3c --- /dev/null +++ b/tests/Rector/v11/v5/RegisterIconToIconFileRector/RegisterIconToIconFileRectorTest.php @@ -0,0 +1,44 @@ +doTestFileInfo($fileInfo); + // This is not accurate. Unfortunately the content of the files are not mutable, so we added multiple times virtually + $this->assertSame(3, $this->removedAndAddedFilesCollector->getAddedFileCount()); + + $addedFilesWithContent = $this->removedAndAddedFilesCollector->getAddedFilesWithContent(); + + $commandsFixture = new SmartFileInfo(__DIR__ . '/Fixture/Expected/Configuration/Icons.php.inc'); + + // Assert that commands file is added + $addedCommandsFile = $addedFilesWithContent[2]; + $this->assertStringContainsString('Icons.php', $addedCommandsFile->getFilePath()); + $this->assertSame($commandsFixture->getContents(), $addedCommandsFile->getFileContent()); + } + + /** + * @return Iterator + */ + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture/my_extension/'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Rector/v11/v5/RegisterIconToIconFileRector/config/configured_rule.php b/tests/Rector/v11/v5/RegisterIconToIconFileRector/config/configured_rule.php new file mode 100644 index 000000000..6e5520966 --- /dev/null +++ b/tests/Rector/v11/v5/RegisterIconToIconFileRector/config/configured_rule.php @@ -0,0 +1,11 @@ +import(__DIR__ . '/../../../../../../config/config_test.php'); + $containerConfigurator->import(Typo3SetList::REGISTER_ICONS_TO_ICON); +}; From 900dee3da71b0e416849126ef3b2adc7fdd3d09c Mon Sep 17 00:00:00 2001 From: Sebastian Schreiber Date: Tue, 5 Apr 2022 10:26:55 +0200 Subject: [PATCH 2/2] TASK: Nicer formatting of Icons.php --- .../AddIconsToReturnRector.php | 13 ++++++++++--- .../Fixture/Expected/Configuration/Icons.php.inc | 6 +++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php b/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php index c8b28da66..5f71c09e8 100644 --- a/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php +++ b/src/Rector/v11/v5/RegisterIconToIconFileRector/AddIconsToReturnRector.php @@ -4,6 +4,7 @@ namespace Ssch\TYPO3Rector\Rector\v11\v5\RegisterIconToIconFileRector; +use PhpParser\Comment; use PhpParser\Node; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayItem; @@ -11,6 +12,7 @@ use PhpParser\Node\Stmt\Return_; use Rector\Core\Contract\Rector\ConfigurableRectorInterface; use Rector\Core\Rector\AbstractRector; +use Rector\NodeTypeResolver\Node\AttributeKey; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; use Webmozart\Assert\Assert; @@ -83,9 +85,14 @@ public function refactor(Node $node): ?Node return null; } - $iconArrayItem = new ArrayItem($this->nodeFactory->createArray($this->iconConfiguration), new String_( - $this->iconIdentifier - )); + $iconArrayItem = new ArrayItem( + $this->nodeFactory->createArray($this->iconConfiguration), + new String_($this->iconIdentifier), + false, + [ + AttributeKey::COMMENTS => [new Comment(PHP_EOL)], + ] + ); $node->expr->items[] = $iconArrayItem; return $node; diff --git a/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc index 881d04a4c..94443ba4d 100644 --- a/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc +++ b/tests/Rector/v11/v5/RegisterIconToIconFileRector/Fixture/Expected/Configuration/Icons.php.inc @@ -1,2 +1,6 @@ ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mybitmap.png'], 'mysvgicon' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mysvg.svg'], 'proof-of-concept' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\FontawesomeIconProvider::class, 'name' => 'spinner', 'spinning' => true]]; +return [ + 'mybitmapicon' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mybitmap.png'], + 'mysvgicon' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, 'source' => 'EXT:my_extension/Resources/Public/Icons/mysvg.svg'], + 'proof-of-concept' => ['provider' => \TYPO3\CMS\Core\Imaging\IconProvider\FontawesomeIconProvider::class, 'name' => 'spinner', 'spinning' => true], +];