From e86a5cc180aa4d04f349d3481c8134cb75786c51 Mon Sep 17 00:00:00 2001 From: Sascha Nowak Date: Sat, 9 Nov 2024 14:46:40 +0100 Subject: [PATCH] feat: TYPO3 13.4 compatibility --- .github/workflows/apply-coding-standard.yml | 19 +-- .github/workflows/ci.yml | 8 +- Classes/Command/GenerateCommand.php | 8 +- Classes/Command/MigrateCommand.php | 2 +- Classes/Command/MigrationExecuteCommand.php | 2 +- Classes/Command/MigrationVersionCommand.php | 2 +- Classes/Command/StatusCommand.php | 2 +- Classes/Service/DependencyFactory.php | 10 +- Classes/Service/DoctrineService.php | 16 +- Classes/Service/Files.php | 6 +- Configuration/Services.php | 2 +- composer.json | 11 +- ext_tables.sql | 7 + rector.php | 164 ++++---------------- 14 files changed, 83 insertions(+), 176 deletions(-) create mode 100644 ext_tables.sql diff --git a/.github/workflows/apply-coding-standard.yml b/.github/workflows/apply-coding-standard.yml index fafec9a..3043eab 100644 --- a/.github/workflows/apply-coding-standard.yml +++ b/.github/workflows/apply-coding-standard.yml @@ -16,7 +16,7 @@ jobs: - name: 'Setup PHP' uses: shivammathur/setup-php@v2 with: - php-version: '8.1' + php-version: '8.3' tools: composer - name: 'Get Composer Cache Directory' @@ -26,7 +26,7 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: 'Cache Composer Dependencies' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: composer @@ -35,9 +35,9 @@ jobs: shell: bash run: composer install --no-interaction - - name: 'Rector PHP Code' - shell: bash - run: composer rector-fix +# - name: 'Rector PHP Code' +# shell: bash +# run: composer rector-fix - name: 'Lint PHP Code' shell: bash @@ -52,8 +52,7 @@ jobs: run: composer test-functional - name: 'Create pull-request' - id: cpr - uses: peter-evans/create-pull-request@v5.0.2 + uses: peter-evans/create-pull-request@v6.0.3 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: "[automated] Apply Coding Standard" @@ -62,9 +61,3 @@ jobs: title: '[automated] Apply Coding Standard' labels: 'automated' delete-branch: true - - - name: 'Enable Pull Request Merge when ready' - if: steps.cpr.outputs.pull-request-operation == 'created' - run: gh pr merge --auto "${{ steps.cpr.outputs.pull-request-number }}" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3c1118..478587c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,12 +20,12 @@ permissions: jobs: test: runs-on: ubuntu-latest - name: TYPO3 v12.4 tests on PHP ${{ matrix.php }} + name: TYPO3 v13.4 tests on PHP ${{ matrix.php }} strategy: fail-fast: false matrix: - php: [ 8.1, 8.2, 8.3 ] + php: [ 8.3 ] steps: - name: 'Checkout code' @@ -46,7 +46,7 @@ jobs: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: 'Cache Composer Dependencies' - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} key: composer-php${{ matrix.php }} @@ -72,7 +72,7 @@ jobs: run: .Build/bin/phpcov merge --html .Build/artifacts/coverage/merged --clover .Build/artifacts/coverage/clover.xml .Build/artifacts/coverage/ - name: 'Generate code coverage summary report' - uses: saschanowak/CloverCodeCoverageSummary@0.4.0 + uses: saschanowak/CloverCodeCoverageSummary@1.1.0 with: filename: .Build/artifacts/coverage/clover.xml diff --git a/Classes/Command/GenerateCommand.php b/Classes/Command/GenerateCommand.php index 2e5ed14..24c19dc 100644 --- a/Classes/Command/GenerateCommand.php +++ b/Classes/Command/GenerateCommand.php @@ -35,11 +35,11 @@ protected function configure(): void ->addArgument('package', InputArgument::OPTIONAL, 'Name of the package', ''); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $packages = array_filter( $this->packageManager->getAvailablePackages(), - fn (PackageInterface $package) => !$package->getPackageMetaData() + fn (PackageInterface $package): bool => !$package->getPackageMetaData() ->isFrameworkType() ); ksort($packages); @@ -84,13 +84,13 @@ public function execute(InputInterface $input, OutputInterface $output): int ucfirst($this->doctrineService->getDatabasePlatformName()), basename($migrationClassPathAndFilename) ); - GeneralUtility::mkdir_deep(dirname((string) $targetPathAndFilename)); + GeneralUtility::mkdir_deep(dirname($targetPathAndFilename)); rename($migrationClassPathAndFilename, $targetPathAndFilename); $output->writeln( sprintf('The migration was moved to: %s', substr( realpath($targetPathAndFilename), - strlen((string) Environment::getProjectPath()) + strlen(Environment::getProjectPath()) )) ); $output->writeln('- Review and adjust the generated migration.'); diff --git a/Classes/Command/MigrateCommand.php b/Classes/Command/MigrateCommand.php index 765a07e..fa30eb1 100644 --- a/Classes/Command/MigrateCommand.php +++ b/Classes/Command/MigrateCommand.php @@ -29,7 +29,7 @@ protected function configure(): void ->addOption('dryRun', null, InputOption::VALUE_NONE, 'Whether to do a dry run or not'); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $result = $this->doctrineService->executeMigrations( version: $this->normalizeVersion($input->getArgument('version')), diff --git a/Classes/Command/MigrationExecuteCommand.php b/Classes/Command/MigrationExecuteCommand.php index a76dd41..0481a35 100644 --- a/Classes/Command/MigrationExecuteCommand.php +++ b/Classes/Command/MigrationExecuteCommand.php @@ -37,7 +37,7 @@ protected function configure(): void ->addOption('dryRun', null, InputOption::VALUE_NONE, 'Whether to do a dry run or not'); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $result = $this->doctrineService->executeMigration( version: $this->normalizeVersion($input->getArgument('version')), diff --git a/Classes/Command/MigrationVersionCommand.php b/Classes/Command/MigrationVersionCommand.php index 159ae59..f00ff88 100644 --- a/Classes/Command/MigrationVersionCommand.php +++ b/Classes/Command/MigrationVersionCommand.php @@ -33,7 +33,7 @@ protected function configure(): void ->addOption('delete', null, InputOption::VALUE_NONE, 'The migration to mark as not migrated'); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { if ($input->getOption('add') === false && $input->getOption('delete') === false) { throw new InvalidArgumentException( diff --git a/Classes/Command/StatusCommand.php b/Classes/Command/StatusCommand.php index 98fec0b..b8426f4 100644 --- a/Classes/Command/StatusCommand.php +++ b/Classes/Command/StatusCommand.php @@ -31,7 +31,7 @@ protected function configure(): void ); } - public function execute(InputInterface $input, OutputInterface $output): int + protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln($this->doctrineService->getFormattedMigrationStatus($input->getOption('showMigrations'))); diff --git a/Classes/Service/DependencyFactory.php b/Classes/Service/DependencyFactory.php index 246d94a..dbf0992 100644 --- a/Classes/Service/DependencyFactory.php +++ b/Classes/Service/DependencyFactory.php @@ -4,6 +4,9 @@ namespace Netlogix\Migrations\Service; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\Migrations\Configuration\Connection\ExistingConnection; use Doctrine\Migrations\Configuration\Migration\ConfigurationArray; use Doctrine\Migrations\DependencyFactory as DoctrineDependencyFactory; @@ -40,13 +43,16 @@ public function createFromConnection(Connection $connection, BufferedOutput $log $connectionLoader = new ExistingConnection($connection); $logger = new ConsoleLogger($logMessages); - $databasePlatformName = ucfirst((string) $connection->getDatabasePlatform()->getName()); $dependencyFactory = DoctrineDependencyFactory::fromConnection($configurationLoader, $connectionLoader); $dependencyFactory->setService( MigrationFinderInterface::class, new MigrationFinder( packageManager: GeneralUtility::makeInstance(PackageManager::class), - databasePlatformName: $databasePlatformName, + databasePlatformName: match (true) { + $connection->getDatabasePlatform() instanceof AbstractMySQLPlatform => 'Mysql', + $connection->getDatabasePlatform() instanceof PostgreSQLPlatform => 'Postgresql', + $connection->getDatabasePlatform() instanceof SQLitePlatform => 'Sqlite', + } ) ); $dependencyFactory->setService(LoggerInterface::class, $logger); diff --git a/Classes/Service/DoctrineService.php b/Classes/Service/DoctrineService.php index 07795df..c687500 100644 --- a/Classes/Service/DoctrineService.php +++ b/Classes/Service/DoctrineService.php @@ -4,6 +4,9 @@ namespace Netlogix\Migrations\Service; +use Doctrine\DBAL\Platforms\AbstractMySQLPlatform; +use Doctrine\DBAL\Platforms\PostgreSQLPlatform; +use Doctrine\DBAL\Platforms\SQLitePlatform; use Doctrine\Migrations\DependencyFactory as DoctrineDependencyFactory; use Doctrine\Migrations\Exception\MigrationClassNotFound; use Doctrine\Migrations\Exception\NoMigrationsFoundWithCriteria; @@ -83,7 +86,7 @@ public function executeMigrations( string $outputPathAndFilename = null, mixed $dryRun = false, bool $quiet = false - ) { + ): string { $migrationRepository = $this->dependencyFactory->getMigrationRepository(); if (count($migrationRepository->getMigrations()) === 0) { return sprintf('The version "%s" can\'t be reached, there are no registered migrations.', $version); @@ -230,9 +233,12 @@ public function generateMigration(): string public function getDatabasePlatformName(): string { - return $this->dependencyFactory->getConnection() - ->getDatabasePlatform() - ->getName(); + $platform = $this->connection->getDatabasePlatform(); + return match (true) { + $platform instanceof AbstractMySQLPlatform => 'Mysql', + $platform instanceof PostgreSQLPlatform => 'Postgresql', + $platform instanceof SQLitePlatform => 'Sqlite', + }; } private function getSortedVersions( @@ -323,7 +329,7 @@ private function mark( $marked = true; } - if ($marked === true) { + if ($marked) { return; } diff --git a/Classes/Service/Files.php b/Classes/Service/Files.php index 17c5da9..b301d3d 100644 --- a/Classes/Service/Files.php +++ b/Classes/Service/Files.php @@ -13,11 +13,7 @@ public static function concatenatePaths(string ...$paths): string $resultingPath = ''; foreach ($paths as $index => $path) { $path = GeneralUtility::fixWindowsFilePath($path); - if ($index === 0) { - $path = rtrim($path, '/'); - } else { - $path = trim($path, '/'); - } + $path = $index === 0 ? rtrim($path, '/') : trim($path, '/'); if ($path !== '') { $resultingPath .= $path . '/'; } diff --git a/Configuration/Services.php b/Configuration/Services.php index 4eb0325..7c2d6ef 100644 --- a/Configuration/Services.php +++ b/Configuration/Services.php @@ -9,7 +9,7 @@ use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; -return function (ContainerConfigurator $containerConfigurator) { +return function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services() ->defaults() ->autowire() diff --git a/composer.json b/composer.json index 5064102..489641b 100644 --- a/composer.json +++ b/composer.json @@ -4,15 +4,14 @@ "type": "typo3-cms-extension", "license": "MIT", "require": { - "php": "^8.1", - "typo3/cms-core": "^12.4", - "doctrine/migrations": "^3.7" + "php": "^8.3", + "typo3/cms-core": "^12.4 | ^13.4", + "doctrine/migrations": "^3.8" }, "require-dev": { "ext-sqlite3": "*", - "ssch/typo3-rector": "^1.5", - "typo3/testing-framework": "^8.0", - "phpunit/phpcov": "^9.0", + "typo3/testing-framework": "^8.0 | ^9.0", + "phpunit/phpcov": "^9.0 | ^10.0", "netlogix/coding-guidelines-php": "^1.0" }, "autoload": { diff --git a/ext_tables.sql b/ext_tables.sql new file mode 100644 index 0000000..92facb1 --- /dev/null +++ b/ext_tables.sql @@ -0,0 +1,7 @@ +CREATE TABLE `sys_doctrine_migrationstatus` ( + version varchar(255) NOT NULL, + executed_at datetime, + execution_time integer, + + PRIMARY KEY (version) +) ENGINE=InnoDB; diff --git a/rector.php b/rector.php index c11e814..b94111f 100644 --- a/rector.php +++ b/rector.php @@ -3,112 +3,46 @@ declare(strict_types=1); use Rector\Config\RectorConfig; -use Rector\Core\Configuration\Option; -use Rector\Core\ValueObject\PhpVersion; -use Rector\Php74\Rector\LNumber\AddLiteralSeparatorToNumberRector; -use Rector\PHPUnit\Set\PHPUnitLevelSetList; +use Rector\Php81\Rector\Array_\FirstClassCallableRector; use Rector\PostRector\Rector\NameImportingPostRector; use Rector\Set\ValueObject\LevelSetList; use Rector\Set\ValueObject\SetList; -use Ssch\TYPO3Rector\Rector\General\ConvertImplicitVariablesToExplicitGlobalsRector; -use Ssch\TYPO3Rector\Rector\General\ExtEmConfRector; -use Ssch\TYPO3Rector\Rector\v11\v0\SubstituteConstantsModeAndRequestTypeRector; -use Ssch\TYPO3Rector\Set\Extension\NimutTestingFrameworkSetList; +use Rector\ValueObject\PhpVersion; +use Ssch\TYPO3Rector\CodeQuality\General\ConvertImplicitVariablesToExplicitGlobalsRector; +use Ssch\TYPO3Rector\Configuration\Typo3Option; use Ssch\TYPO3Rector\Set\Typo3LevelSetList; -use Ssch\TYPO3Rector\Set\Typo3SetList; - -return static function (RectorConfig $rectorConfig): void { - - // If you want to override the number of spaces for your typoscript files you can define it here, the default value is 4 - // $parameters = $rectorConfig->parameters(); - // $parameters->set(Typo3Option::TYPOSCRIPT_INDENT_SIZE, 2); - - $rectorConfig->sets([ - // PHP - LevelSetList::UP_TO_PHP_81, +return RectorConfig::configure() + ->withPaths([ + __DIR__ . '/Classes', + __DIR__ . '/Configuration', + __DIR__ . '/Tests', + ]) + ->withPhpSets( + true + ) + ->withSets([ + LevelSetList::UP_TO_PHP_83, SetList::CODE_QUALITY, SetList::CODING_STYLE, SetList::DEAD_CODE, SetList::TYPE_DECLARATION, SetList::EARLY_RETURN, SetList::INSTANCEOF, - - // PhpUnit - PHPUnitLevelSetList::UP_TO_PHPUNIT_100, - - // TYPO3 - // v7 - Typo3SetList::TCA_76, - - // v8 - Typo3SetList::TCA_87, - - // v9 - Typo3SetList::TCA_95, - Typo3SetList::EXTBASE_COMMAND_CONTROLLERS_TO_SYMFONY_COMMANDS, - - // v10 - Typo3SetList::TCA_104, - Typo3SetList::TYPOSCRIPT_CONDITIONS_95, - Typo3SetList::TYPOSCRIPT_100, - Typo3SetList::TYPOSCRIPT_CONDITIONS_104, - Typo3SetList::DATABASE_TO_DBAL, - - // v11 - Typo3SetList::TCA_114, - Typo3SetList::REGISTER_ICONS_TO_ICON, - - // v12 - Typo3SetList::TCA_120, - Typo3SetList::TCA_123, - Typo3SetList::TYPOSCRIPT_120, - - // Typo3LevelSetList::UP_TO_TYPO3_7, - // Typo3LevelSetList::UP_TO_TYPO3_8, - // Typo3LevelSetList::UP_TO_TYPO3_9, - // Typo3LevelSetList::UP_TO_TYPO3_10, - // Typo3LevelSetList::UP_TO_TYPO3_11, - Typo3LevelSetList::UP_TO_TYPO3_12, - - NimutTestingFrameworkSetList::NIMUT_TESTING_FRAMEWORK_TO_TYPO3_TESTING_FRAMEWORK - ]); - - $rectorConfig->rule(SubstituteConstantsModeAndRequestTypeRector::class); - - // Define your target version which you want to support - $rectorConfig->phpVersion(PhpVersion::PHP_81); - - $parameters = $rectorConfig->parameters(); - $parameters->set(Option::IMPORT_SHORT_CLASSES, false); - $parameters->set(Option::AUTO_IMPORT_DOC_BLOCK_NAMES, false); - - // If you only want to process one/some TYPO3 extension(s), you can specify its path(s) here. - // If you use the option --config change __DIR__ to getcwd() - $rectorConfig->paths([ - __DIR__, - ]); - - // When you use rector there are rules that require some more actions like creating UpgradeWizards for outdated TCA types. - // To fully support you we added some warnings. So watch out for them. - - // If you use importNames(), you should consider excluding some TYPO3 files. - $rectorConfig->skip([ - // PHP - AddLiteralSeparatorToNumberRector::class, - - // TYPO3 + Typo3LevelSetList::UP_TO_TYPO3_13, + ]) + # To have a better analysis from PHPStan, we teach it here some more things + ->withPHPStanConfigs([ + Typo3Option::PHPSTAN_FOR_RECTOR_PATH + ]) + ->withPhpVersion(PhpVersion::PHP_83) + ->withRules([ + ConvertImplicitVariablesToExplicitGlobalsRector::class, + ]) + # If you use importNames(), you should consider excluding some TYPO3 files. + ->withSkip([ // @see https://github.com/sabbelasichon/typo3-rector/issues/2536 - __DIR__ . '/**/Configuration/ExtensionBuilder/*', - // We skip those directories on purpose as there might be node_modules or similar - // that include typescript which would result in false positive processing - __DIR__ . '/**/Resources/**/node_modules/*', - __DIR__ . '/**/Resources/**/NodeModules/*', - __DIR__ . '/**/Resources/**/BowerComponents/*', - __DIR__ . '/**/Resources/**/bower_components/*', - __DIR__ . '/**/Resources/**/build/*', - __DIR__ . '/**/Resources/**/App/*', - __DIR__ . '/**/Resources/Public/*', + __DIR__ . '/Resources/*', __DIR__ . '/vendor/*', __DIR__ . '/Build/*', __DIR__ . '/public/*', @@ -118,42 +52,8 @@ 'ext_localconf.php', 'ext_tables.php', 'ClassAliasMap.php', - __DIR__ . '/**/Configuration/*.php', - __DIR__ . '/**/Configuration/**/*.php', - ] + ], + FirstClassCallableRector::class => [ + __DIR__ . '/Configuration/Services.php', + ], ]); - - // If you have trouble that rector cannot run because some TYPO3 constants are not defined add an additional constants file - // @see https://github.com/sabbelasichon/typo3-rector/blob/main/typo3.constants.php - // @see https://github.com/rectorphp/rector/blob/main/docs/static_reflection_and_autoload.md#include-files - // $parameters->set(Option::BOOTSTRAP_FILES, [ - // __DIR__ . '/typo3.constants.php' - // ]); - - // register a single rule - // $rectorConfig->rule(\Ssch\TYPO3Rector\Rector\v9\v0\InjectAnnotationRector::class); - - /** - * Useful rule from RectorPHP itself to transform i.e. GeneralUtility::makeInstance('TYPO3\CMS\Core\Log\LogManager') - * to GeneralUtility::makeInstance(\TYPO3\CMS\Core\Log\LogManager::class) calls. - * But be warned, sometimes it produces false positives (edge cases), so watch out - */ - // $rectorConfig->rule(\Rector\Php55\Rector\String_\StringClassNameToClassConstantRector::class); - - // Optional non-php file functionalities: - // @see https://github.com/sabbelasichon/typo3-rector/blob/main/docs/beyond_php_file_processors.md - - // Rewrite your extbase persistence class mapping from typoscript into php according to official docs. - // This processor will create a summarized file with all the typoscript rewrites combined into a single file. - /* $rectorConfig->ruleWithConfiguration(\Ssch\TYPO3Rector\FileProcessor\TypoScript\Rector\v10\v0\ExtbasePersistenceTypoScriptRector::class, [ - \Ssch\TYPO3Rector\FileProcessor\TypoScript\Rector\v10\v0\ExtbasePersistenceTypoScriptRector::FILENAME => __DIR__ . '/packages/acme_demo/Configuration/Extbase/Persistence/Classes.php', - ]); */ - // Add some general TYPO3 rules - $rectorConfig->rule(ConvertImplicitVariablesToExplicitGlobalsRector::class); - $rectorConfig->ruleWithConfiguration(ExtEmConfRector::class, [ - ExtEmConfRector::ADDITIONAL_VALUES_TO_BE_REMOVED => [] - ]); - - // Modernize your TypoScript include statements for files and move from to @import use the FileIncludeToImportStatementVisitor (introduced with TYPO3 9.0) - // $rectorConfig->rule(\Ssch\TYPO3Rector\FileProcessor\TypoScript\Rector\v9\v0\FileIncludeToImportStatementTypoScriptRector::class); -};