diff --git a/CHANGELOG.md b/CHANGELOG.md index a773f78..166ed4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,51 @@ # Changelog +## v2.0.0 - December 2024 -## [Unreleased] - 2024-05-21 +This version is a major release and includes breaking changes. +Adjust your configuration, apply the fixes and review code for the new version. + +### PER Coding Style 2.0 +PER Coding Style 2.0 (https://www.php-fig.org/per/coding-style/) was introduced. +This leads to some adjustments in the code which most probably can be autofixed with `ecs check --fix`. +Review your code after running the fixer. + +Rules applied to the set can be found here: https://cs.symfony.com/doc/ruleSets/PER-CS2.0.html. This is the base from which we make our own small adjustments. +"Concat Space" for example, is a rule which has already been applied to the Symfony rule set in the past, which we enforce in the standard everywhere. + +### Unify rule sets +Removed `whatwedo-wordpress.php` and `whatwedo-symfony.php` in favor of `whatwedo-common.php`. If you have used `whatwedo-symfony.php` or `whatwedo-wordpress.php`, you have to switch to `whatwedo-common.php` in the config. + +### Update ECS Configuration +Update your configuration (`ecs.php`) to the new version and adjust to your preferences. See our example file in the root of this repository. + +### Enforce types via PHP instead of DocBlocks +`PhpdocToReturnTypeFixer` and `PhpdocToParamTypeFixer` will enforce the types in the PHP code instead of the DocBlocks. +If you don't use PHPStan or similar for static type checking, that could be a problem for you since those types can be wrong. You can disable these fixers in your local configuration if you can't migrate them properly for now. + +## Technical + +### Changed +- Unify rule sets (https://github.com/whatwedo/PhpCodingStandard/issues/17) +- Minor update to `symplify/easy-coding-standard 12.4` + +### Added +- DynamicSet `@PER-CS2.0` was added to the configuration (https://github.com/whatwedo/PhpCodingStandard/issues/19) +- Add `MultilinePromotedPropertiesFixer` (https://github.com/whatwedo/PhpCodingStandard/issues/27) +- Add `MultilinePromotedPropertiesFixer` (https://github.com/whatwedo/PhpCodingStandard/issues/27) +- Add `PhpdocToReturnTypeFixer` and `PhpdocToParamTypeFixer` +- `NoDoctrineMigrationsGeneratedCommentFixer` is now part of the default configuration (https://github.com/whatwedo/PhpCodingStandard/issues/16) +- `ConcatSpaceFixer` => `'spacing' => 'none'` is now part of the default configuration (https://github.com/whatwedo/PhpCodingStandard/issues/15) + +### Removed +- Remove deprecated `NoTrailingCommaInListCallFixer` +- `AssignmentInConditionSniff` is skipped + +--- + +## v1.2.5 - March 2024 ### Changed -- update to `symplify/easy-coding-standard ^12` +- Update to `symplify/easy-coding-standard ^12` +- Dump Fixer added diff --git a/README.md b/README.md index 67310be..8bde6ee 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ # PhpCodingStandard This project is a set of coding standard rules, which we are using at [whatwedo](https://whatwedo.ch). It's heavily based on [Simplify/EasyCodingStandard](https://github.com/Symplify/EasyCodingStandard). +It's based on PER Coding Style 2.0 (https://www.php-fig.org/per/coding-style/). ## Installation @@ -20,15 +21,13 @@ composer require whatwedo/php-coding-standard You can run the checks without project specific configuration using one of following commands: ``` -vendor/bin/ecs check SRC_DIRECTORY --config vendor/whatwedo/php-coding-standard/config/whatwedo-symfony.php # Symfony projects -vendor/bin/ecs check SRC_DIRECTORY --config vendor/whatwedo/php-coding-standard/config/whatwedo-wordpress.php # WordPress projects -vendor/bin/ecs check SRC_DIRECTORY --config vendor/whatwedo/php-coding-standard/config/whatwedo-common.php # Common PHP projects +vendor/bin/ecs check SRC_DIRECTORY --config vendor/whatwedo/php-coding-standard/config/whatwedo-common.php ``` - ### With custom configuration -If you want to add additional checkers or exclude files, you have to create an `ecs.php` file in your own project root directory. +But we suggest to create an `ecs.php` file in your own project root directory. +There's a sample configuration file in the root of this repository. ```php skip() - $ecsConfig->skip([ + // Remove rules with $config->skip() + $config->skip([ SlevomatCodingStandard\Sniffs\Variables\UnusedVariableSniff::class => null, // Explicitly remove some rules in a specific files @@ -50,7 +49,7 @@ return static function (ECSConfig $ecsConfig): void { */ // This need to come last - $ecsConfig->sets([__DIR__ . '/vendor/whatwedo/php-coding-standard/config/whatwedo-common.php']); + $config->sets([__DIR__ . '/vendor/whatwedo/php-coding-standard/config/whatwedo-common.php']); }; ``` @@ -64,6 +63,27 @@ To fix certain issues automatically add `--fix` add the end For other configuration options, check out [Simplify/EasyCodingStandard](https://github.com/Symplify/EasyCodingStandard). +## Usage with PHP CS Fixer *Experimental* + +add `.php-cs-fixer.dist.php` in your project + +```php +=7.4", "kubawerlos/php-cs-fixer-custom-fixers": "^3.0", - "slevomat/coding-standard": "^8.5", - "symplify/easy-coding-standard": "^12.1" + "slevomat/coding-standard": "^8.15", + "symplify/easy-coding-standard": "^12.4" }, "autoload": { "psr-4": { diff --git a/ecs.php b/ecs.php index 5d8a49f..35beac3 100644 --- a/ecs.php +++ b/ecs.php @@ -4,9 +4,11 @@ use Symplify\EasyCodingStandard\Config\ECSConfig; -return static function (ECSConfig $ecsConfig): void { - $ecsConfig->paths([ - __DIR__ . '/', +return static function (ECSConfig $config): void { + $config->paths([ + 'src', + 'tests', ]); - $ecsConfig->import('config/whatwedo-common.php'); + $config->skip([]); + $config->import('vendor/whatwedo/php-coding-standard/config/whatwedo-common.php'); }; diff --git a/src/PhpCsFixerConfig/BaseCsFixerConfig.php b/src/PhpCsFixerConfig/BaseCsFixerConfig.php new file mode 100644 index 0000000..1113a9e --- /dev/null +++ b/src/PhpCsFixerConfig/BaseCsFixerConfig.php @@ -0,0 +1,51 @@ + ['@Symfony' => true], + 10000 => ['strict_param' => true], + 11001 => [ + 'function_declaration' => [ + 'closure_fn_spacing' => 'none' + ], + 'phpdoc_to_return_type' => true, + 'phpdoc_to_param_type' => true, + \PhpCsFixerCustomFixers\Fixer\NoNullableBooleanTypeFixer::name() => true, + \PhpCsFixerCustomFixers\Fixer\MultilinePromotedPropertiesFixer::name() => true, + 'single_line_empty_body' => true, //reset @Syfmony setting + 'trailing_comma_in_multiline' => [ //reset @Syfmony setting + 'after_heredoc' => true, + 'elements' => [ + 'arguments', + 'array_destructuring', + 'arrays', + 'match', + 'parameters' + ] + ], + 'phpdoc_to_comment' => false, + 'single_line_throw' => false, + ], + + ]; + } + + public static function getExcludes(): array + { + return []; + } + + public static function configure(ConfigInterface $config): void + { + $config->registerCustomFixers(new \PhpCsFixerCustomFixers\Fixers()); + } + + +} diff --git a/src/PhpCsFixerConfig/WwdPhpCsFixerConfigInterface.php b/src/PhpCsFixerConfig/WwdPhpCsFixerConfigInterface.php new file mode 100644 index 0000000..a6c3468 --- /dev/null +++ b/src/PhpCsFixerConfig/WwdPhpCsFixerConfigInterface.php @@ -0,0 +1,17 @@ +> + */ + public static function getRules(): array; + + public static function getExcludes(): array; + + public static function configure(ConfigInterface $config): void; +} diff --git a/src/PhpCsFixerConfigBuilder.php b/src/PhpCsFixerConfigBuilder.php new file mode 100644 index 0000000..466dc22 --- /dev/null +++ b/src/PhpCsFixerConfigBuilder.php @@ -0,0 +1,108 @@ +[] $configs + */ + public static function build( + string $projectDir, + array $configs + ): ParallelAwareConfigInterface + { + $excludes = self::buildExcludes($configs); + + $finder = new PhpCsFixer\Finder(); + $finder->in($projectDir) + ->exclude($excludes); + + $config = new PhpCsFixer\Config(); + self::configure($config, $configs); + $config->setFinder($finder) + ->setRiskyAllowed(true) + ->setRules(self::buildRules($configs)); + + return $config; + } + + private static function getNextOrder(array $rules, int $order) + { + while (array_key_exists($order, $rules)) { + $order++; + } + return $order; + } + + /** + * @param class-string[] $configs + */ + private static function buildRules(array $configs): array + { + /** + * @var array > + */ + $rulesGroups = []; + + foreach ($configs as $config) { + foreach ($config::getRules() as $order => $configRule) { + $rulesGroups[self::getNextOrder($rulesGroups, $order)] = $configRule; + } + } + + // order rules from Configs + ksort($rulesGroups); + + $rules = []; + foreach ($rulesGroups as $rulesGroup) { + foreach ($rulesGroup as $rule => $ruleSetting) { + $rules[$rule] = $ruleSetting; + } + } + + return $rules; + } + + /** + * @param class-string[] $configs + */ + public static function dumpRules(array $configs) + { + var_dump(self::buildRules($configs)); + } + + /** + * @param class-string[] $configs + */ + public static function dumpExcludes(array $configs) + { + var_dump(self::buildExcludes($configs)); + } + + /** + * @param class-string[] $configs + */ + private static function buildExcludes(array $configs): array + { + $excludes = []; + foreach ($configs as $config) { + foreach ($config::getExcludes() as $configExclude) { + $excludes[] = $configExclude; + } + } + return $excludes; + } + + private static function configure(PhpCsFixer\Config $config, array $configs) + { + /** @var WwdPhpCsFixerConfigInterface $configurationSet */ + foreach ($configs as $configurationSet) { + $configurationSet::configure($config); + } + } +}