Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/php cs fixer config #33

Open
wants to merge 12 commits into
base: release/v2.0.0
Choose a base branch
from
47 changes: 45 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
38 changes: 29 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -20,26 +21,24 @@ 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
<?php
declare(strict_types=1);

use Symplify\EasyCodingStandard\Config\ECSConfig;

return static function (ECSConfig $ecsConfig): void {
return static function (ECSConfig $config): void {
/*
// Remove rules with $ecsConfig->skip()
$ecsConfig->skip([
// Remove rules with $config->skip()
$config->skip([
SlevomatCodingStandard\Sniffs\Variables\UnusedVariableSniff::class => null,

// Explicitly remove some rules in a specific files
Expand All @@ -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']);
};
```

Expand All @@ -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
<?php

use whatwedo\PhpCodingStandard\PhpCsFixerConfig\BaseCsFixerConfig;
use whatwedo\PhpCodingStandard\PhpCsFixerConfigBuilder;

$configs = [
BaseCsFixerConfig::class,
];
//PhpCsFixerConfigBuilder::dumpRules($configs);
//PhpCsFixerConfigBuilder::dumpExcludes($configs);


$config = PhpCsFixerConfigBuilder::build(__DIR__, $configs);

return $config;
```

## Dependencies

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
"require": {
"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": {
Expand Down
10 changes: 6 additions & 4 deletions ecs.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
};
51 changes: 51 additions & 0 deletions src/PhpCsFixerConfig/BaseCsFixerConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

namespace whatwedo\PhpCodingStandard\PhpCsFixerConfig;

use PhpCsFixer\ConfigInterface;

class BaseCsFixerConfig implements WwdPhpCsFixerConfigInterface
{
public static function getRules(): array
{
return [
100 => ['@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());
}


}
17 changes: 17 additions & 0 deletions src/PhpCsFixerConfig/WwdPhpCsFixerConfigInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace whatwedo\PhpCodingStandard\PhpCsFixerConfig;

use PhpCsFixer\ConfigInterface;

interface WwdPhpCsFixerConfigInterface
{
/**
* @var array <int, <array<string, mixed>>
*/
public static function getRules(): array;

public static function getExcludes(): array;

public static function configure(ConfigInterface $config): void;
}
108 changes: 108 additions & 0 deletions src/PhpCsFixerConfigBuilder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
<?php

namespace whatwedo\PhpCodingStandard;

use PhpCsFixer;
use PhpCsFixer\ParallelAwareConfigInterface;
use whatwedo\PhpCodingStandard\PhpCsFixerConfig\WwdPhpCsFixerConfigInterface;

class PhpCsFixerConfigBuilder
{
/**
* @param class-string<WwdPhpCsFixerConfigInterface>[] $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<WwdPhpCsFixerConfigInterface>[] $configs
*/
private static function buildRules(array $configs): array
{
/**
* @var array <int, <array<string, mixed>>
*/
$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<WwdPhpCsFixerConfigInterface>[] $configs
*/
public static function dumpRules(array $configs)
{
var_dump(self::buildRules($configs));
}

/**
* @param class-string<WwdPhpCsFixerConfigInterface>[] $configs
*/
public static function dumpExcludes(array $configs)
{
var_dump(self::buildExcludes($configs));
}

/**
* @param class-string<WwdPhpCsFixerConfigInterface>[] $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);
}
}
}