diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index b549c41cf8..49b1a0ce71 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -32,10 +32,6 @@ jobs: name: 'Tests' run: vendor/bin/phpunit - - - name: 'PHP Linter' - run: vendor/bin/parallel-lint src tests - - name: 'Check Active Classes' run: vendor/bin/class-leak check bin src --ansi diff --git a/README.md b/README.md index fa706a15a4..ae23da3b9d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Killer Features -- Install on **any PHP 7.2-PHP 8.3** project with any dependencies +- Install on **any PHP 7.2-PHP 8.4** project with any dependencies - Blazing fast with parallel run out of the box - Use [PHP_CodeSniffer or PHP-CS-Fixer](https://tomasvotruba.com/blog/2017/05/03/combine-power-of-php-code-sniffer-and-php-cs-fixer-in-3-lines/) - anything you like - Use **prepared sets** and [PHP CS Fixer sets](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/blob/master/doc/ruleSets/index.rst) to save time diff --git a/build/rector-downgrade-php-72.php b/build/rector-downgrade-php-72.php index 0f32817a5a..e80b098913 100644 --- a/build/rector-downgrade-php-72.php +++ b/build/rector-downgrade-php-72.php @@ -3,15 +3,11 @@ declare(strict_types=1); use Rector\Config\RectorConfig; -use Rector\Set\ValueObject\DowngradeLevelSetList; -return static function (RectorConfig $rectorConfig): void { - $rectorConfig->parallel(); - $rectorConfig->sets([DowngradeLevelSetList::DOWN_TO_PHP_72]); - - $rectorConfig->skip([ +return RectorConfig::configure() + ->withDowngradeSets(php72: true) + ->withSkip([ '*/Tests/*', '*/tests/*', '*/Fixtures/DirectoryExpansion/.hiddenAbove/*' ]); -}; diff --git a/build/target-repository/preload.php b/build/target-repository/preload.php index 43788e8d78..acd074b428 100644 --- a/build/target-repository/preload.php +++ b/build/target-repository/preload.php @@ -7,7 +7,5 @@ require_once __DIR__ . '/vendor/psr/container/src/ContainerExceptionInterface.php'; require_once __DIR__ . '/vendor/psr/container/src/NotFoundExceptionInterface.php'; require_once __DIR__ . '/vendor/psr/container/src/ContainerInterface.php'; -require_once __DIR__ . '/vendor/symplify/rule-doc-generator-contracts/src/ValueObject/RuleDefinition.php'; -require_once __DIR__ . '/vendor/symplify/rule-doc-generator-contracts/src/Contract/DocumentedRuleInterface.php'; require_once __DIR__ . '/vendor/react/promise/src/functions.php'; require_once __DIR__ . '/vendor/symfony/deprecation-contracts/function.php'; diff --git a/composer.json b/composer.json index 6710fa79f9..c00de95ddc 100644 --- a/composer.json +++ b/composer.json @@ -15,30 +15,29 @@ "php": ">=8.2", "composer/pcre": "^3.3.2", "composer/xdebug-handler": "^3.0.5", - "friendsofphp/php-cs-fixer": "^3.68.5", - "illuminate/container": "^11.40", + "friendsofphp/php-cs-fixer": "^3.75.0", + "illuminate/container": "^12.10", "nette/utils": "^4.0.5", "sebastian/diff": "^6.0.2", - "squizlabs/php_codesniffer": "^3.11.3", - "symfony/console": "^6.4.17", + "squizlabs/php_codesniffer": "^3.13", + "symfony/console": "^6.4.20", "symfony/finder": "^7.2.2", - "symplify/coding-standard": "^12.2.3", + "symplify/coding-standard": "^12.4.3", "symplify/easy-parallel": "^11.2.2", "webmozart/assert": "^1.11" }, "require-dev": { - "php-parallel-lint/php-parallel-lint": "^1.4", "phpstan/extension-installer": "^1.4.3", "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", "phpstan/phpstan-webmozart-assert": "^2.0", - "phpunit/phpunit": "^11.5.2", - "rector/rector": "^2.0.7", - "rector/type-perfect": "^2.0.1", + "phpunit/phpunit": "^11.5", + "rector/rector": "^2.0.12", + "rector/type-perfect": "^2.0.2", "symplify/phpstan-extensions": "^12.0", - "symplify/vendor-patches": "^11.3.7", + "symplify/vendor-patches": "^11.4", "tomasvotruba/class-leak": "^2.0", - "tomasvotruba/type-coverage": "^2.0.1", + "tomasvotruba/type-coverage": "^2.0.2", "tomasvotruba/unused-public": "^2.0", "tracy/tracy": "^2.10.9" }, diff --git a/config/set/symplify.php b/config/set/symplify.php index 36a27ddb07..3d7efbfc0a 100644 --- a/config/set/symplify.php +++ b/config/set/symplify.php @@ -3,7 +3,9 @@ declare(strict_types=1); use PhpCsFixer\Fixer\Phpdoc\GeneralPhpdocAnnotationRemoveFixer; +use Symplify\CodingStandard\Fixer\Annotation\RemoveMethodNameDuplicateDescriptionFixer; use Symplify\CodingStandard\Fixer\Annotation\RemovePHPStormAnnotationFixer; +use Symplify\CodingStandard\Fixer\Annotation\RemovePropertyVariableNameDescriptionFixer; use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayListItemNewlineFixer; use Symplify\CodingStandard\Fixer\ArrayNotation\ArrayOpenerAndCloserNewlineFixer; use Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer; @@ -21,6 +23,8 @@ RemovePHPStormAnnotationFixer::class, ParamReturnAndVarTagMalformsFixer::class, RemoveUselessDefaultCommentFixer::class, + RemoveMethodNameDuplicateDescriptionFixer::class, + RemovePropertyVariableNameDescriptionFixer::class, // arrays ArrayListItemNewlineFixer::class, diff --git a/ecs.php b/ecs.php index a86e80b472..06b91faf50 100644 --- a/ecs.php +++ b/ecs.php @@ -2,13 +2,11 @@ declare(strict_types=1); -use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; return ECSConfig::configure() ->withPaths([__DIR__ . '/bin', __DIR__ . '/config', __DIR__ . '/src', __DIR__ . '/tests']) ->withEditorConfig() - ->withRules([LineLengthFixer::class]) ->withRootFiles() ->withSkip(['*/Source/*', '*/Fixture/*']) ->withPreparedSets(symplify: true, psr12: true, common: true); diff --git a/full_ecs_build.sh b/full_ecs_build.sh index 4b23a8f572..495208ea29 100644 --- a/full_ecs_build.sh +++ b/full_ecs_build.sh @@ -11,7 +11,7 @@ rm -rf tests vendor/phpcsstandards/php_codesniffer/tests vendor/phpcsstandards/p # downgrade with rector mkdir rector-local -composer require rector/rector --working-dir rector-local +composer require rector/rector --working-dir rector-local --dev rector-local/vendor/bin/rector process bin config/config.php src vendor --config build/rector-downgrade-php-72.php --ansi # prefix diff --git a/phpstan.neon b/phpstan.neon index 078787d9fd..468e0e2e92 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,7 +1,7 @@ parameters: level: 8 - reportUnmatchedIgnoredErrors: false + # reportUnmatchedIgnoredErrors: false # requires exact closure types checkMissingCallableSignature: true @@ -13,7 +13,6 @@ parameters: - rector.php excludePaths: - # deprecated, to be removed - scoper.php # tests @@ -56,7 +55,6 @@ parameters: - tests/Skipper/Skipper/Skipper/SkipperTest.php - tests/Skipper/Skipper/Skip/SkipSkipperTest.php - tests/Skipper/SkipCriteriaResolver/SkippedPathsResolver/SkippedPathsResolverTest.php - - tests/FixerRunner/Application/FileProcessorTest.ph - src/Testing/PHPUnit/AbstractCheckerTestCase.php # optional @@ -78,3 +76,8 @@ parameters: # hack to autoload contants - '#Call to new PHP_CodeSniffer\\Util\\Tokens\(\) on a separate line has no effect#' + + # php version condition + - + identifier: smaller.alwaysFalse + path: src/Configuration/ConfigInitializer.php diff --git a/rector.php b/rector.php index 4056f8a2f5..bb35d35b9c 100644 --- a/rector.php +++ b/rector.php @@ -3,6 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; +use Rector\DeadCode\Rector\ConstFetch\RemovePhpVersionIdCheckRector; return RectorConfig::configure() ->withPhpSets() @@ -24,4 +25,7 @@ '*/Fixture/*', __DIR__ . '/src/SniffRunner/ValueObject/File.php', __DIR__ . '/scoper.php', + + // conditional checks + RemovePhpVersionIdCheckRector::class, ]); diff --git a/src/Config/ECSConfig.php b/src/Config/ECSConfig.php index 80b7fdcd4e..6bd1e9d4bc 100644 --- a/src/Config/ECSConfig.php +++ b/src/Config/ECSConfig.php @@ -19,7 +19,6 @@ use Symplify\EasyCodingStandard\DependencyInjection\CompilerPass\RemoveMutualCheckersCompilerPass; use Symplify\EasyCodingStandard\DependencyInjection\SimpleParameterProvider; use Symplify\EasyCodingStandard\ValueObject\Option; -use Symplify\RuleDocGenerator\Contract\ConfigurableRuleInterface; use Webmozart\Assert\Assert; use Webmozart\Assert\InvalidArgumentException; @@ -105,7 +104,7 @@ public function ruleWithConfiguration(string $checkerClass, array $configuration $this->autowireWhitespaceAwareFixer($checkerClass); if (is_a($checkerClass, FixerInterface::class, true)) { - Assert::isAnyOf($checkerClass, [ConfigurableFixerInterface::class, ConfigurableRuleInterface::class]); + Assert::isAOf($checkerClass, ConfigurableFixerInterface::class); $this->extend($checkerClass, static function (ConfigurableFixerInterface $configurableFixer) use ( $configuration ): ConfigurableFixerInterface { diff --git a/src/Configuration/ConfigInitializer.php b/src/Configuration/ConfigInitializer.php index 0818716c82..cfdb7d6001 100644 --- a/src/Configuration/ConfigInitializer.php +++ b/src/Configuration/ConfigInitializer.php @@ -37,7 +37,7 @@ public function createConfig(string $projectDirectory): void // config already exists, nothing to add if ($doesConfigExist) { $this->symfonyStyle->warning( - 'We found ecs.php config, but with no rules in it. Register some rules or sets there first' + 'We found ecs.php config, but no rules in it. Register some rules or sets there first' ); return; } @@ -52,10 +52,8 @@ public function createConfig(string $projectDirectory): void $templateFileContents = FileSystem::read(__DIR__ . '/../../templates/ecs.php.dist'); - $projectPhpDirectories = $this->initPathsResolver->resolve($projectDirectory); - $projectPhpDirectoriesContents = $this->createPathsString($projectPhpDirectories); - - $templateFileContents = str_replace('__PATHS__', $projectPhpDirectoriesContents, $templateFileContents); + $templateFileContents = $this->fillPaths($projectDirectory, $templateFileContents); + $templateFileContents = $this->fillPreparedSets($projectDirectory, $templateFileContents); // create the ecs.php file FileSystem::write(getcwd() . '/ecs.php', $templateFileContents, null); @@ -63,6 +61,14 @@ public function createConfig(string $projectDirectory): void $this->symfonyStyle->success('The ecs.php config was generated! Re-run the command to tidy your code'); } + private function fillPaths(string $projectDirectory, string $templateFileContents): string + { + $projectPhpDirectories = $this->initPathsResolver->resolve($projectDirectory); + $projectPhpDirectoriesContents = $this->createPathsString($projectPhpDirectories); + + return str_replace('__PATHS__', $projectPhpDirectoriesContents, $templateFileContents); + } + /** * @param string[] $projectPhpDirectories */ @@ -75,4 +81,18 @@ private function createPathsString(array $projectPhpDirectories): string return rtrim($projectPhpDirectoriesContents); } + + private function fillPreparedSets(string $projectDirectory, string $templateFileContents): string + { + $templateFileContents = $this->fillPaths($projectDirectory, $templateFileContents); + + if (PHP_VERSION_ID < 80000) { + $preparedSetTemplate = FileSystem::read(__DIR__ . '/../../templates/include/prepared_sets_php74.php.inc'); + } else { + // PHP 8.0+ uses named arguments + $preparedSetTemplate = FileSystem::read(__DIR__ . '/../../templates/include/prepared_sets_php80.php.inc'); + } + + return str_replace('__PREPARED_SETS__', rtrim($preparedSetTemplate), $templateFileContents); + } } diff --git a/src/Configuration/ECSConfigBuilder.php b/src/Configuration/ECSConfigBuilder.php index 0272d4dd04..79f913cb4f 100644 --- a/src/Configuration/ECSConfigBuilder.php +++ b/src/Configuration/ECSConfigBuilder.php @@ -21,6 +21,7 @@ use Symplify\EasyCodingStandard\Configuration\EditorConfig\EndOfLine; use Symplify\EasyCodingStandard\Configuration\EditorConfig\IndentStyle; use Symplify\EasyCodingStandard\Configuration\EditorConfig\QuoteType; +use Symplify\EasyCodingStandard\Exception\Configuration\InitializationException; use Symplify\EasyCodingStandard\Exception\Configuration\SuperfluousConfigurationException; use Symplify\EasyCodingStandard\ValueObject\Option; use Symplify\EasyCodingStandard\ValueObject\Set\SetList; @@ -221,6 +222,12 @@ public function withPreparedSets( /** @see SetList::CLEAN_CODE */ bool $cleanCode = false, ): self { + if (func_get_args() === []) { + throw new InitializationException( + 'Pick at least one set in "->withPreparedSets()" in your ecs.php using named arguments, e.g. "->withPreparedSets(spaces: true)"' + ); + } + if ($psr12) { $this->sets[] = SetList::PSR_12; } @@ -229,9 +236,20 @@ public function withPreparedSets( // include all "common" sets $this->sets[] = SetList::COMMON; - if ($arrays || $spaces || $namespaces || $docblocks || $controlStructures || $phpunit || $comments) { + if (($alreadyIncludedSets = array_keys(array_filter([ + 'arrays' => $arrays, + 'spaces' => $spaces, + 'namespaces' => $namespaces, + 'docblocks' => $docblocks, + 'controlStructures' => $controlStructures, + 'phpunit' => $phpunit, + 'comments' => $comments, + ]))) !== []) { throw new SuperfluousConfigurationException( - 'This set is already included in the "common" set. You can remove it' + sprintf( + 'The following sets are already included in the "common" set: %s. Please remove them.', + implode(', ', $alreadyIncludedSets) + ) ); } } else { @@ -307,6 +325,7 @@ public function withPhpCsFixerSets( bool $php81Migration = false, bool $php82Migration = false, bool $php83Migration = false, + bool $php84Migration = false, bool $phpunit30MigrationRisky = false, bool $phpunit32MigrationRisky = false, bool $phpunit35MigrationRisky = false, @@ -423,6 +442,10 @@ public function withPhpCsFixerSets( $this->dynamicSets[] = '@PHP83Migration'; } + if ($php84Migration) { + $this->dynamicSets[] = '@PHP84Migration'; + } + if ($phpunit30MigrationRisky) { $this->dynamicSets[] = '@PHPUnit30Migration:risky'; } diff --git a/src/Configuration/EditorConfig/EditorConfigFactory.php b/src/Configuration/EditorConfig/EditorConfigFactory.php index 0a21d4fb54..353c86ac72 100644 --- a/src/Configuration/EditorConfig/EditorConfigFactory.php +++ b/src/Configuration/EditorConfig/EditorConfigFactory.php @@ -44,7 +44,7 @@ public function parse(string $editorConfigFileContents): EditorConfig trimTrailingWhitespace: $this->field($config, 'trim_trailing_whitespace', $this->id(...)), insertFinalNewline: $this->field($config, 'insert_final_newline', $this->id(...)), maxLineLength: $this->field($config, 'max_line_length', $this->id(...)), - quoteType: $config['quote_type'] ?? null, + quoteType: $config['quote_type'] ?? null ); } diff --git a/src/SniffRunner/Application/SniffFileProcessor.php b/src/SniffRunner/Application/SniffFileProcessor.php index fac85ad767..f261744eec 100644 --- a/src/SniffRunner/Application/SniffFileProcessor.php +++ b/src/SniffRunner/Application/SniffFileProcessor.php @@ -9,6 +9,7 @@ use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\AssignmentInConditionSniff; use PHP_CodeSniffer\Standards\Generic\Sniffs\CodeAnalysis\UnusedFunctionParameterSniff; +use PHP_CodeSniffer\Standards\Generic\Sniffs\Commenting\TodoSniff; use PHP_CodeSniffer\Standards\PSR2\Sniffs\Classes\PropertyDeclarationSniff; use PHP_CodeSniffer\Standards\PSR2\Sniffs\Methods\MethodDeclarationSniff; use PHP_CodeSniffer\Standards\Squiz\Sniffs\PHP\CommentedOutCodeSniff; @@ -44,6 +45,7 @@ final class SniffFileProcessor implements FileProcessorInterface MethodDeclarationSniff::class, CommentedOutCodeSniff::class, UnusedFunctionParameterSniff::class, + TodoSniff::class, ]; /** diff --git a/templates/ecs.php.dist b/templates/ecs.php.dist index 3631ce5ba1..f6f57eb6d5 100644 --- a/templates/ecs.php.dist +++ b/templates/ecs.php.dist @@ -15,13 +15,7 @@ __PATHS__ NoUnusedImportsFixer::class, ]) - // add sets - group of rules - // ->withPreparedSets( - // arrays: true, - // namespaces: true, - // spaces: true, - // docblocks: true, - // comments: true, - // ) - - ; + // add sets - group of rules, from easiest to more complex ones + // uncomment one, apply one, commit, PR, merge and repeat +__PREPARED_SETS__ + ; diff --git a/templates/include/prepared_sets_php74.php.inc b/templates/include/prepared_sets_php74.php.inc new file mode 100644 index 0000000000..2f86f7b0bc --- /dev/null +++ b/templates/include/prepared_sets_php74.php.inc @@ -0,0 +1,7 @@ + ->withSets([ + // \Symplify\EasyCodingStandard\ValueObject\Set\SetList::SPACES, + // \Symplify\EasyCodingStandard\ValueObject\Set\SetList::NAMESPACES, + // \Symplify\EasyCodingStandard\ValueObject\Set\SetList::DOCBLOCKS, + // \Symplify\EasyCodingStandard\ValueObject\Set\SetList::ARRAYS, + // \Symplify\EasyCodingStandard\ValueObject\Set\SetList::COMMENTS, + ]) diff --git a/templates/include/prepared_sets_php80.php.inc b/templates/include/prepared_sets_php80.php.inc new file mode 100644 index 0000000000..18023261e9 --- /dev/null +++ b/templates/include/prepared_sets_php80.php.inc @@ -0,0 +1,7 @@ + ->withPreparedSets( + // spaces: true, + // namespaces: true, + // docblocks: true, + // arrays: true, + // comments: true, + ) diff --git a/tests/Set/Psr12/Fixture/fixture.php.inc b/tests/Set/Psr12/Fixture/fixture.php.inc index 1a8a5d979a..a8afa19c41 100644 --- a/tests/Set/Psr12/Fixture/fixture.php.inc +++ b/tests/Set/Psr12/Fixture/fixture.php.inc @@ -142,8 +142,8 @@ factoryFunction($arg1, $arg2, [ declare(strict_types=1); namespace Vendor\Package; -use Vendor\Package\SomeNamespace\ClassD as D; use Vendor\Package\{ClassA as A, ClassB, ClassC as C}; +use Vendor\Package\SomeNamespace\ClassD as D; use function Vendor\Package\{functionA, functionB, functionC}; use const Vendor\Package\{ConstantA, ConstantB, ConstantC};