Skip to content
Open
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);
}
}
}