From 6d5d3b9976965faab2ccb7f1e9fe177b587c6294 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Sat, 27 Apr 2024 19:13:38 +0200 Subject: [PATCH 01/36] Feat: Add PhpdocToCommentFixer (part of #94) --- ecs-internal.php | 2 +- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 7 +++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 7 +++++++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ecs-internal.php b/ecs-internal.php index 6384521..944036e 100644 --- a/ecs-internal.php +++ b/ecs-internal.php @@ -6,7 +6,7 @@ use Symplify\CodingStandard\Fixer\LineLength\LineLengthFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; -/** +/* * Internal rules configuration for the lmc/coding-standard project itself */ return ECSConfig::configure() diff --git a/ecs.php b/ecs.php index 671686f..9f4f6d5 100644 --- a/ecs.php +++ b/ecs.php @@ -106,6 +106,7 @@ use PhpCsFixer\Fixer\Phpdoc\PhpdocReturnSelfReferenceFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocScalarFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocSingleLineVarSpacingFixer; +use PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocTrimFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocTypesFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocVarAnnotationCorrectOrderFixer; @@ -332,6 +333,8 @@ PhpdocSingleLineVarSpacingFixer::class, // PHPDoc should start and end with content PhpdocTrimFixer::class, + // Docblocks should only be used on structural elements. + PhpdocToCommentFixer::class, // The correct case must be used for standard PHP types in PHPDoc. PhpdocTypesFixer::class, // `@var` and `@type` annotations must have type and name in the correct order diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 1d11928..816b34d 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -26,6 +26,13 @@ class Basic public function fooBar(mixed $foo): mixed { + // PhpdocToCommentFixer + /* + * Phpdoc used instead of plain comment + */ + if ($foo === 'bar') { + $baz = 'bat'; + } // TernaryToElvisOperatorFixer return ($foo ?: 'not true'); } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 34081b7..e713cb1 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -22,6 +22,13 @@ class Basic public function fooBar(mixed $foo): mixed { + // PhpdocToCommentFixer + /** + * Phpdoc used instead of plain comment + */ + if ($foo === 'bar') { + $baz = 'bat'; + } // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true'); } From 04e0a4960298e827c697467a3db10d1969cf733c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Sat, 27 Apr 2024 19:26:03 +0200 Subject: [PATCH 02/36] Feat: Enable all aliases in NoAliasFunctionsFixer (part of #94) --- ecs.php | 4 ++-- tests/Integration/Fixtures/Basic.correct.php.inc | 4 +++- tests/Integration/Fixtures/Basic.wrong.php.inc | 4 +++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ecs.php b/ecs.php index 9f4f6d5..2071a35 100644 --- a/ecs.php +++ b/ecs.php @@ -215,8 +215,6 @@ ArrayPushFixer::class, // Replace non multibyte-safe functions with corresponding mb function MbStrFunctionsFixer::class, - // Master functions shall be used instead of aliases - NoAliasFunctionsFixer::class, // Replaces `rand`, `srand`, `getrandmax` functions calls with their `mt_*` analogs RandomApiMigrationFixer::class, // Cast shall be used, not `settype()` @@ -458,6 +456,8 @@ 'var_dump' => null, ], ]) + // Master functions shall be used instead of aliases + ->withConfiguredRule(NoAliasFunctionsFixer::class, ['sets' => ['@all']]) // There should be exactly one blank line before a namespace declaration. ->withConfiguredRule(BlankLinesBeforeNamespaceFixer::class, ['min_line_breaks' => 2, 'max_line_breaks' => 2]) // Proper operator spacing diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 816b34d..a7406f6 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -31,8 +31,10 @@ class Basic * Phpdoc used instead of plain comment */ if ($foo === 'bar') { - $baz = 'bat'; + // NoAliasFunctionsFixer + $baz = implode(',', ['foo', 'bar']); } + // TernaryToElvisOperatorFixer return ($foo ?: 'not true'); } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index e713cb1..43a80d8 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -27,8 +27,10 @@ class Basic * Phpdoc used instead of plain comment */ if ($foo === 'bar') { - $baz = 'bat'; + // NoAliasFunctionsFixer + $baz = join(',', ['foo', 'bar']); } + // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true'); } From b78d8e6231a7cdc865f1f9062438f50bf58e7769 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Sun, 28 Apr 2024 23:42:04 +0200 Subject: [PATCH 03/36] Feat: Replace UnionTypeHintFormatSniff with TypesSpacesFixer --- ecs.php | 6 +++--- tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc | 2 +- tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ecs.php b/ecs.php index 2071a35..7f8b221 100644 --- a/ecs.php +++ b/ecs.php @@ -135,6 +135,7 @@ use PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer; use PhpCsFixer\Fixer\Whitespace\NoWhitespaceInBlankLineFixer; use PhpCsFixer\Fixer\Whitespace\TypeDeclarationSpacesFixer; +use PhpCsFixer\Fixer\Whitespace\TypesSpacesFixer; use SlevomatCodingStandard\Sniffs\Classes\RequireConstructorPropertyPromotionSniff; use SlevomatCodingStandard\Sniffs\ControlStructures\RequireNullSafeObjectOperatorSniff; use SlevomatCodingStandard\Sniffs\Exceptions\ReferenceThrowableOnlySniff; @@ -143,7 +144,6 @@ use SlevomatCodingStandard\Sniffs\TypeHints\ParameterTypeHintSniff; use SlevomatCodingStandard\Sniffs\TypeHints\PropertyTypeHintSniff; use SlevomatCodingStandard\Sniffs\TypeHints\ReturnTypeHintSniff; -use SlevomatCodingStandard\Sniffs\TypeHints\UnionTypeHintFormatSniff; use Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; @@ -263,6 +263,8 @@ FopenFlagsFixer::class, // Add missing space between function's argument and its typehint. TypeDeclarationSpacesFixer::class, + // None space should be around union type and intersection type operators. + TypesSpacesFixer::class, // Function `implode` must be called with 2 arguments in the documented order. ImplodeCallFixer::class, // Lambda must not import variables it doesn't use. @@ -505,8 +507,6 @@ 'use_trait', ], ]) - // Format union types - ->withConfiguredRule(UnionTypeHintFormatSniff::class, ['withSpaces' => 'no']) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc index c6e2726..a2cbc45 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc @@ -9,7 +9,7 @@ class NewPhpFeatures } public function php80features( - string|bool $foo, // UnionTypeHintFormatSniff + string|bool $foo, // TypesSpacesFixer int $bar, // RequireTrailingCommaInDeclarationSniff ): string|bool { $value = mt_rand( diff --git a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc index 49d1d08..f4c786b 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc @@ -12,7 +12,7 @@ class NewPhpFeatures } public function php80features( - string | bool $foo, // UnionTypeHintFormatSniff + string | bool $foo, // TypesSpacesFixer int $bar // RequireTrailingCommaInDeclarationSniff ): string | bool { $value = mt_rand( From 2eeb811108b731e5ce0530e51df7c956077de352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Sun, 28 Apr 2024 23:54:42 +0200 Subject: [PATCH 04/36] Feat: Add AssignNullCoalescingToCoalesceEqualFixer (part of #94) --- tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc | 3 +++ tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc index a2cbc45..4805d68 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc @@ -20,6 +20,9 @@ class NewPhpFeatures $dateOrNull = $this->mayReturnDateTimeOrNull(); $timestamp = $dateOrNull?->getTimestamp(); // RequireNullSafeObjectOperatorSniff + // AssignNullCoalescingToCoalesceEqualFixer + $name = $_GET['name'] ?? 'default'; + return $foo; } diff --git a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc index f4c786b..e3486bc 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc @@ -23,6 +23,9 @@ class NewPhpFeatures $dateOrNull = $this->mayReturnDateTimeOrNull(); $timestamp = $dateOrNull !== null ? $dateOrNull->getTimestamp() : null; // RequireNullSafeObjectOperatorSniff + // AssignNullCoalescingToCoalesceEqualFixer + $name = isset($_GET['name']) ? $_GET['name'] : 'default'; + return $foo; } From fa1ce8ba6958409499b0b2631a0f6896c118f236 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 16:21:14 +0200 Subject: [PATCH 05/36] Feat: Add NoSpaceAroundDoubleColonFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 4 +++- tests/Integration/Fixtures/Basic.wrong.php.inc | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/ecs.php b/ecs.php index 7f8b221..a3afa64 100644 --- a/ecs.php +++ b/ecs.php @@ -87,6 +87,7 @@ use PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer; use PhpCsFixer\Fixer\Operator\ConcatSpaceFixer; use PhpCsFixer\Fixer\Operator\NewWithParenthesesFixer; +use PhpCsFixer\Fixer\Operator\NoSpaceAroundDoubleColonFixer; use PhpCsFixer\Fixer\Operator\ObjectOperatorWithoutWhitespaceFixer; use PhpCsFixer\Fixer\Operator\StandardizeNotEqualsFixer; use PhpCsFixer\Fixer\Operator\TernaryOperatorSpacesFixer; @@ -295,6 +296,8 @@ NoLeadingNamespaceWhitespaceFixer::class, BinaryOperatorSpacesFixer::class, + // There must be no space around scope resolution double colons + NoSpaceAroundDoubleColonFixer::class, NewWithParenthesesFixer::class, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index a7406f6..03f2bdf 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -4,7 +4,7 @@ class Basic { public const FOO = 'foo'; // ClassAttributesSeparationFixer - public function isEqual($a, $b) // VisibilityRequiredFixer + public function isEqual($a, ?string $b): ?bool // VisibilityRequiredFixer, CompactNullableTypeDeclarationFixer { // TrimArraySpacesFixer $fooBar = ['a', 'b']; @@ -16,6 +16,8 @@ class Basic $uselessSprintf = 'bar'; // StrictParamFixer $useStrictParam = in_array(1337, $fooBar, true); + // NoSpaceAroundDoubleColonFixer + $className = DateTime::class; // SingleSpaceAfterConstructFixer, StrictComparisonFixer if ($a === $b || $bazLength !== 3) { return true; diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 43a80d8..5538205 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -3,7 +3,7 @@ class Basic { const FOO = 'foo'; // ClassAttributesSeparationFixer - function isEqual($a, $b) // VisibilityRequiredFixer + function isEqual($a, ? string $b): ? bool // VisibilityRequiredFixer, CompactNullableTypeDeclarationFixer { // TrimArraySpacesFixer $fooBar = [ 'a', 'b']; @@ -15,6 +15,8 @@ class Basic $uselessSprintf = sprintf('bar'); // StrictParamFixer $useStrictParam = in_array(1337, $fooBar); + // NoSpaceAroundDoubleColonFixer + $className = DateTime :: class; // SingleSpaceAfterConstructFixer, StrictComparisonFixer if ($a == $b || $bazLength != 3) { return true; } return false; // BlankLineBeforeStatementFixer From 42257c662232a29fafa97b5e92d8b1c94aec5b67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 17:31:25 +0200 Subject: [PATCH 06/36] Feat: Add ClassReferenceNameCasingFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 2 ++ tests/Integration/Fixtures/Basic.wrong.php.inc | 2 ++ 3 files changed, 7 insertions(+) diff --git a/ecs.php b/ecs.php index a3afa64..470302e 100644 --- a/ecs.php +++ b/ecs.php @@ -46,6 +46,7 @@ use PhpCsFixer\Fixer\Basic\BracesFixer; use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer; use PhpCsFixer\Fixer\Basic\PsrAutoloadingFixer; +use PhpCsFixer\Fixer\Casing\ClassReferenceNameCasingFixer; use PhpCsFixer\Fixer\Casing\MagicMethodCasingFixer; use PhpCsFixer\Fixer\Casing\NativeFunctionCasingFixer; use PhpCsFixer\Fixer\Casing\NativeTypeDeclarationCasingFixer; @@ -232,6 +233,8 @@ WhitespaceAfterCommaInArrayFixer::class, // Classes must be in a path that matches their namespace PsrAutoloadingFixer::class, + // When referencing an internal class it must be written using the correct casing. + ClassReferenceNameCasingFixer::class, // Magic method definitions and calls must be using the correct casing MagicMethodCasingFixer::class, // Function defined by PHP should be called using the correct casing diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 03f2bdf..a289b66 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -18,6 +18,8 @@ class Basic $useStrictParam = in_array(1337, $fooBar, true); // NoSpaceAroundDoubleColonFixer $className = DateTime::class; + // ClassReferenceNameCasingFixer + $date = new \DateTime(); // SingleSpaceAfterConstructFixer, StrictComparisonFixer if ($a === $b || $bazLength !== 3) { return true; diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 5538205..b7c1364 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -17,6 +17,8 @@ class Basic $useStrictParam = in_array(1337, $fooBar); // NoSpaceAroundDoubleColonFixer $className = DateTime :: class; + // ClassReferenceNameCasingFixer + $date = new \datetime(); // SingleSpaceAfterConstructFixer, StrictComparisonFixer if ($a == $b || $bazLength != 3) { return true; } return false; // BlankLineBeforeStatementFixer From 7e52ba66edc076725bff8f9c7515ee61753d907a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 18:10:22 +0200 Subject: [PATCH 07/36] Feat: Add NoUnneededImportAliasFixer (part of #94) --- ecs.php | 11 +++++++---- tests/Integration/Fixtures/Basic.correct.php.inc | 5 +++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 5 +++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ecs.php b/ecs.php index 470302e..e8849c4 100644 --- a/ecs.php +++ b/ecs.php @@ -76,6 +76,7 @@ use PhpCsFixer\Fixer\FunctionNotation\ReturnTypeDeclarationFixer; use PhpCsFixer\Fixer\FunctionNotation\VoidReturnFixer; use PhpCsFixer\Fixer\Import\NoLeadingImportSlashFixer; +use PhpCsFixer\Fixer\Import\NoUnneededImportAliasFixer; use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; use PhpCsFixer\Fixer\Import\OrderedImportsFixer; use PhpCsFixer\Fixer\LanguageConstruct\DeclareEqualNormalizeFixer; @@ -281,13 +282,15 @@ ReturnTypeDeclarationFixer::class, VoidReturnFixer::class, - + // Remove leading slashes in `use` clauses. NoLeadingImportSlashFixer::class, - + // Imports should not be aliased as the same name. + NoUnneededImportAliasFixer::class, + // Unused `use` statements must be removed. NoUnusedImportsFixer::class, - + // Order `use` statements. OrderedImportsFixer::class, - + // Equal sign in declare statement should not be surrounded by spaces. DeclareEqualNormalizeFixer::class, // Replaces `is_null($var)` expression with `null === $var` IsNullFixer::class, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index a289b66..f1b4c98 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -1,5 +1,7 @@ Date: Mon, 6 May 2024 18:15:30 +0200 Subject: [PATCH 08/36] Feat: Add SingleLineCommentSpacingFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 4 ++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/ecs.php b/ecs.php index e8849c4..4e27a97 100644 --- a/ecs.php +++ b/ecs.php @@ -59,6 +59,7 @@ use PhpCsFixer\Fixer\ClassNotation\SingleTraitInsertPerStatementFixer; use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer; use PhpCsFixer\Fixer\Comment\NoEmptyCommentFixer; +use PhpCsFixer\Fixer\Comment\SingleLineCommentSpacingFixer; use PhpCsFixer\Fixer\ControlStructure\NoUselessElseFixer; use PhpCsFixer\Fixer\ControlStructure\SwitchContinueToBreakFixer; use PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer; @@ -256,6 +257,8 @@ SingleTraitInsertPerStatementFixer::class, // There should not be any empty comments NoEmptyCommentFixer::class, + // Single-line comments must have proper spacing. + SingleLineCommentSpacingFixer::class, // There should not be useless `else` cases NoUselessElseFixer::class, // Switch case must not be ended with `continue` but with `break`. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index f1b4c98..a1059c2 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -44,6 +44,10 @@ class Basic $baz = implode(',', ['foo', 'bar']); } + // SingleLineCommentSpacingFixer + // This comment should have space on the beginning + /* So should this one, also with space on the end */ + // TernaryToElvisOperatorFixer return ($foo ?: 'not true'); } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index bf1624f..b798de0 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -40,6 +40,10 @@ class Basic $baz = join(',', ['foo', 'bar']); } + // SingleLineCommentSpacingFixer + //This comment should have space on the beginning + /*So should this one, also with space on the end*/ + // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true'); } From 80b71e26a49791dfd3135c3a7475f27a08fb6f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 18:33:21 +0200 Subject: [PATCH 09/36] Feat: Add OrderedClassElementsFixer with specified order of elements (part of #94) --- ecs.php | 18 ++++++++++++++++++ .../Integration/Fixtures/Basic.correct.php.inc | 5 +++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/ecs.php b/ecs.php index 4e27a97..2cd6af7 100644 --- a/ecs.php +++ b/ecs.php @@ -55,6 +55,7 @@ use PhpCsFixer\Fixer\CastNotation\ShortScalarCastFixer; use PhpCsFixer\Fixer\ClassNotation\ClassAttributesSeparationFixer; use PhpCsFixer\Fixer\ClassNotation\NoBlankLinesAfterClassOpeningFixer; +use PhpCsFixer\Fixer\ClassNotation\OrderedClassElementsFixer; use PhpCsFixer\Fixer\ClassNotation\SelfAccessorFixer; use PhpCsFixer\Fixer\ClassNotation\SingleTraitInsertPerStatementFixer; use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer; @@ -519,6 +520,23 @@ 'use_trait', ], ]) + // Elements of classes/interfaces/traits/enums should be in the defined order + ->withConfiguredRule( + OrderedClassElementsFixer::class, + [ + 'order' => [ + 'use_trait', + 'case', // enum values should be before other elements + 'constant', + 'property', + 'construct', + 'destruct', + 'magic', + 'phpunit', // phpunit special methods like setUp should be before test methods + 'method', + ], + ], + ) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index a1059c2..c81bd52 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -4,8 +4,13 @@ use Bar\Foo; // NoUnneededImportAliasFixer class Basic { + use SomeUsefulTrait; // OrderedClassElementsFixer public const FOO = 'foo'; // ClassAttributesSeparationFixer + public const MY_PUBLIC_CONST = 333; // OrderedClassElementsFixer + + protected int $myProperty = 666; // OrderedClassElementsFixer + public function isEqual($a, ?string $b): ?bool // VisibilityRequiredFixer, CompactNullableTypeDeclarationFixer { // TrimArraySpacesFixer diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index b798de0..47a1b82 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -29,6 +29,8 @@ class Basic return false; // BlankLineBeforeStatementFixer } + public const MY_PUBLIC_CONST = 333; // OrderedClassElementsFixer + public function fooBar(mixed $foo): mixed { // PhpdocToCommentFixer @@ -47,4 +49,8 @@ class Basic // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true'); } + + protected int $myProperty = 666; // OrderedClassElementsFixer + + use SomeUsefulTrait; // OrderedClassElementsFixer } From c7dc404d64f85e12af9296395f795f88a134bc66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 19:20:13 +0200 Subject: [PATCH 10/36] Test: Add tests for NoTrailingCommaInSinglelineFixer and array declarations --- ecs.php | 2 +- .../Fixtures/Basic.correct.php.inc | 21 +++++++++++++++++++ .../Integration/Fixtures/Basic.wrong.php.inc | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+), 1 deletion(-) diff --git a/ecs.php b/ecs.php index 2cd6af7..1c4627e 100644 --- a/ecs.php +++ b/ecs.php @@ -226,7 +226,7 @@ SetTypeToCastFixer::class, // Array index should always be written by using square braces NormalizeIndexBraceFixer::class, - // PHP single-line arrays should not have trailing comma + // Values separated by a comma on a single line should not have a trailing comma. NoTrailingCommaInSinglelineFixer::class, // Multi-line arrays, arguments list and parameters list must have a trailing comma TrailingCommaInMultilineFixer::class, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index c81bd52..660849a 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -15,6 +15,8 @@ class Basic { // TrimArraySpacesFixer $fooBar = ['a', 'b']; + // NoTrailingCommaInSinglelineFixer + mb_strlen('foobar'); // MbStrFunctionsFixer $bazLength = mb_strlen('baz'); // LambdaNotUsedImportFixer @@ -56,4 +58,23 @@ class Basic // TernaryToElvisOperatorFixer return ($foo ?: 'not true'); } + + public function arrayDeclarations(): void + { + $empty = []; + + $singleLineArray = ['foo', 'bar', 'baz']; + $singleLineArray2 = [1, 2, 3]; + + $multiLineAssociative = [ + 'foo' => 'bar', + 'baz' => 'bat', + ]; + + $multiLineAssociative2 = [ + 'firstKey' => 'bar', + 'thisIsSecondKey' => 'bat', + 'third' => 'bat', + ]; + } } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 47a1b82..fbe5470 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -9,6 +9,8 @@ class Basic { // TrimArraySpacesFixer $fooBar = [ 'a', 'b']; + // NoTrailingCommaInSinglelineFixer + mb_strlen('foobar', ); // MbStrFunctionsFixer $bazLength = strlen('baz'); // LambdaNotUsedImportFixer @@ -53,4 +55,23 @@ class Basic protected int $myProperty = 666; // OrderedClassElementsFixer use SomeUsefulTrait; // OrderedClassElementsFixer + + public function arrayDeclarations(): void + { + $empty = array(); + + $singleLineArray = ['foo', 'bar', 'baz',]; + $singleLineArray2 = [1,2,3]; + + $multiLineAssociative = [ + 'foo'=>'bar', + 'baz'=>'bat' + ]; + + $multiLineAssociative2 = [ + 'firstKey' => 'bar', + 'thisIsSecondKey' => 'bat', + 'third' => 'bat', + ]; + } } From c04b71593b148a809dee4cc33651444c6daaaf20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 23:54:15 +0200 Subject: [PATCH 11/36] Feat: Add PhpdocAlignFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 13 +++++++++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 14 ++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/ecs.php b/ecs.php index 1c4627e..4573c36 100644 --- a/ecs.php +++ b/ecs.php @@ -102,6 +102,7 @@ use PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer; use PhpCsFixer\Fixer\Phpdoc\NoSuperfluousPhpdocTagsFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocAddMissingParamAnnotationFixer; +use PhpCsFixer\Fixer\Phpdoc\PhpdocAlignFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocIndentFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocNoAccessFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocNoEmptyReturnFixer; @@ -499,6 +500,8 @@ 'allow_unused_params' => false, // whether param annotation without actual signature is allowed 'remove_inheritdoc' => true, // remove @inheritDoc tags ]) + // All items of the given PHPDoc tags must be left-aligned. + ->withConfiguredRule(PhpdocAlignFixer::class, ['align' => 'left']) // Order phpdoc tags by value. ->withConfiguredRule(PhpdocOrderByValueFixer::class, ['annotations' => ['covers', 'group', 'throws']]) // Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type (`$this->...`) diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 660849a..53347a4 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -77,4 +77,17 @@ class Basic 'third' => 'bat', ]; } + + /** + * Very well documented method. + * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. + * @param int|float $second Second parameter does have a comment, unlike the first one. + * @param string|null $third Third parameter is optional and has a default value. + * @throws \Exception + * @return mixed There is also information about return type. + */ + public function veryWellDocumented(string $first, int|float $second, ?string $third = '3rd'): mixed + { + return $first . $third; + } } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index fbe5470..3d1e53d 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -74,4 +74,18 @@ class Basic 'third' => 'bat', ]; } + + /** + * Very well documented method. + * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. + * @param string $first + * @throws \Exception + * @param int|float $second Second parameter does have a comment, unlike the first one. + * @param string|null $third Third parameter is optional and has a default value. + * @return mixed There is also information about return type. + */ + public function veryWellDocumented(string $first, int|float $second, ?string $third = '3rd'): mixed + { + return $first . $third; + } } From 46ebf2e5c6efdb937f46aaf45991720cfbd51b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 6 May 2024 23:58:00 +0200 Subject: [PATCH 12/36] Feat: Change phpdoc order so that `@throws` is after `@return` --- ecs.php | 4 ++-- tests/Integration/Fixtures/Basic.correct.php.inc | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ecs.php b/ecs.php index 4573c36..39750e1 100644 --- a/ecs.php +++ b/ecs.php @@ -337,8 +337,6 @@ PhpdocNoEmptyReturnFixer::class, // `@package` and `@subpackage` annotations should be omitted from PHPDoc. PhpdocNoPackageFixer::class, - // Annotations in PHPDoc should be ordered. - PhpdocOrderFixer::class, // The type of `@return` annotations of methods returning a reference to itself must the configured one. PhpdocReturnSelfReferenceFixer::class, // Scalar types should always be written in the same form. @@ -502,6 +500,8 @@ ]) // All items of the given PHPDoc tags must be left-aligned. ->withConfiguredRule(PhpdocAlignFixer::class, ['align' => 'left']) + // Annotations in PHPDoc should be ordered in defined sequence. + ->withConfiguredRule(PhpdocOrderFixer::class, ['order' => ['param', 'return', 'throws']]) // Order phpdoc tags by value. ->withConfiguredRule(PhpdocOrderByValueFixer::class, ['annotations' => ['covers', 'group', 'throws']]) // Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type (`$this->...`) diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 53347a4..f698845 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -83,8 +83,8 @@ class Basic * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. * @param int|float $second Second parameter does have a comment, unlike the first one. * @param string|null $third Third parameter is optional and has a default value. - * @throws \Exception * @return mixed There is also information about return type. + * @throws \Exception */ public function veryWellDocumented(string $first, int|float $second, ?string $third = '3rd'): mixed { From b16a39839ead129f39b43a28e01dad410d0a7a87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 00:10:08 +0200 Subject: [PATCH 13/36] Feat: Add ArrayIndentationFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 8 +++++++- tests/Integration/Fixtures/Basic.wrong.php.inc | 11 ++++++++--- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ecs.php b/ecs.php index 39750e1..067392b 100644 --- a/ecs.php +++ b/ecs.php @@ -135,6 +135,7 @@ use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use PhpCsFixer\Fixer\Strict\StrictParamFixer; use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer; +use PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer; use PhpCsFixer\Fixer\Whitespace\BlankLineBeforeStatementFixer; use PhpCsFixer\Fixer\Whitespace\CompactNullableTypeDeclarationFixer; use PhpCsFixer\Fixer\Whitespace\HeredocIndentationFixer; @@ -385,6 +386,8 @@ StrictComparisonFixer::class, // Convert double quotes to single quotes for simple strings SingleQuoteFixer::class, + // Each element of an array must be indented exactly once. + ArrayIndentationFixer::class, // Remove extra spaces in a nullable typehint CompactNullableTypeDeclarationFixer::class, // Heredoc/nowdoc content must be properly indented. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index f698845..0d21c71 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -66,12 +66,18 @@ class Basic $singleLineArray = ['foo', 'bar', 'baz']; $singleLineArray2 = [1, 2, 3]; - $multiLineAssociative = [ + $multiLineAssociative1 = [ 'foo' => 'bar', 'baz' => 'bat', ]; $multiLineAssociative2 = [ + 'foo' => 'bar', + 'baz' => 'bat', + 'bak' => 'baz', + ]; + + $multiLineAssociative3 = [ 'firstKey' => 'bar', 'thisIsSecondKey' => 'bat', 'third' => 'bat', diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 3d1e53d..24ba5fb 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -63,12 +63,17 @@ class Basic $singleLineArray = ['foo', 'bar', 'baz',]; $singleLineArray2 = [1,2,3]; - $multiLineAssociative = [ - 'foo'=>'bar', - 'baz'=>'bat' + $multiLineAssociative1 = [ + 'foo' => 'bar', 'baz' => 'bat', ]; $multiLineAssociative2 = [ + 'foo'=>'bar', + 'baz'=>'bat', + 'bak'=>'baz' + ]; + + $multiLineAssociative3 = [ 'firstKey' => 'bar', 'thisIsSecondKey' => 'bat', 'third' => 'bat', From ce973e3bed2b9aa048027b6ec898ff77b626ce40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 00:39:20 +0200 Subject: [PATCH 14/36] Feat: Remove space after `fn` declaration as defined in PER2.0 --- ecs.php | 6 ++++++ tests/Integration/Fixtures/Basic.correct.php.inc | 10 +++++++++- tests/Integration/Fixtures/Basic.wrong.php.inc | 10 +++++++++- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/ecs.php b/ecs.php index 067392b..ab97c09 100644 --- a/ecs.php +++ b/ecs.php @@ -68,6 +68,7 @@ use PhpCsFixer\Fixer\FunctionNotation\CombineNestedDirnameFixer; use PhpCsFixer\Fixer\FunctionNotation\FopenFlagOrderFixer; use PhpCsFixer\Fixer\FunctionNotation\FopenFlagsFixer; +use PhpCsFixer\Fixer\FunctionNotation\FunctionDeclarationFixer; use PhpCsFixer\Fixer\FunctionNotation\ImplodeCallFixer; use PhpCsFixer\Fixer\FunctionNotation\LambdaNotUsedImportFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUnreachableDefaultArgumentValueFixer; @@ -543,6 +544,11 @@ ], ], ) + // Spaces should be properly placed in a function declaration. + ->withConfiguredRule( + FunctionDeclarationFixer::class, + ['closure_fn_spacing' => 'none'], // Defined in PER 2.0 + ) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 0d21c71..db6f21f 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -40,8 +40,16 @@ class Basic return false; // BlankLineBeforeStatementFixer } - public function fooBar(mixed $foo): mixed + public function fooBar(mixed $foo): mixed // FunctionDeclarationFixer { + $value = 5; + // FunctionDeclarationFixer + $function = function ($foo) use ($value) { + return $foo + $value; + }; + // FunctionDeclarationFixer + $fn = fn($foo) => $foo + $value; + // PhpdocToCommentFixer /* * Phpdoc used instead of plain comment diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 24ba5fb..6d15347 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -33,8 +33,16 @@ class Basic public const MY_PUBLIC_CONST = 333; // OrderedClassElementsFixer - public function fooBar(mixed $foo): mixed + public function fooBar ( mixed $foo ): mixed // FunctionDeclarationFixer { + $value = 5; + // FunctionDeclarationFixer + $function = function($foo)use($value) { + return $foo + $value; + }; + // FunctionDeclarationFixer + $fn = fn ($foo) => $foo + $value; + // PhpdocToCommentFixer /** * Phpdoc used instead of plain comment From 8caa26a68874f0d6e92f3c83958d2fa224e23ebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 00:50:40 +0200 Subject: [PATCH 15/36] Feat: Empty body of function should be abberviated `{}` as defined in PER2.0 (part of #94) --- ecs.php | 3 +++ src/Helper/SniffClassWrapper.php | 4 +--- tests/Integration/Fixtures/Basic.correct.php.inc | 6 ++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 8 ++++++++ tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc | 3 +-- 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/ecs.php b/ecs.php index ab97c09..cc9cfce 100644 --- a/ecs.php +++ b/ecs.php @@ -46,6 +46,7 @@ use PhpCsFixer\Fixer\Basic\BracesFixer; use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer; use PhpCsFixer\Fixer\Basic\PsrAutoloadingFixer; +use PhpCsFixer\Fixer\Basic\SingleLineEmptyBodyFixer; use PhpCsFixer\Fixer\Casing\ClassReferenceNameCasingFixer; use PhpCsFixer\Fixer\Casing\MagicMethodCasingFixer; use PhpCsFixer\Fixer\Casing\NativeFunctionCasingFixer; @@ -229,6 +230,8 @@ SetTypeToCastFixer::class, // Array index should always be written by using square braces NormalizeIndexBraceFixer::class, + // Empty body of class, interface, trait, enum or function must be abbreviated as {} and placed on the same line as the previous symbol, separated by a single space. + SingleLineEmptyBodyFixer::class, // Defined in PER 2.0 // Values separated by a comma on a single line should not have a trailing comma. NoTrailingCommaInSinglelineFixer::class, // Multi-line arrays, arguments list and parameters list must have a trailing comma diff --git a/src/Helper/SniffClassWrapper.php b/src/Helper/SniffClassWrapper.php index 84d05f2..470fda6 100644 --- a/src/Helper/SniffClassWrapper.php +++ b/src/Helper/SniffClassWrapper.php @@ -33,9 +33,7 @@ final class SniffClassWrapper { - public function __construct(private File $file, private int $position, private Naming $naming) - { - } + public function __construct(private File $file, private int $position, private Naming $naming) {} public function getClassName(): ?string { diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index db6f21f..8ad27a2 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -104,4 +104,10 @@ class Basic { return $first . $third; } + + public function emptyFunction1(): void {} // SingleLineEmptyBodyFixer + + public function emptyFunction2( + $arg, + ): void {} // SingleLineEmptyBodyFixer } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 6d15347..ef49723 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -101,4 +101,12 @@ class Basic { return $first . $third; } + + public function emptyFunction1(): void { + } // SingleLineEmptyBodyFixer + + public function emptyFunction2( + $arg + ): void { + } // SingleLineEmptyBodyFixer } diff --git a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc index 4805d68..2b0cba1 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc @@ -5,8 +5,7 @@ namespace Lmc\CodingStandard\Integration\Fixtures; class NewPhpFeatures { public function __construct(private string $someString) // RequireConstructorPropertyPromotionSniff - { - } + {} public function php80features( string|bool $foo, // TypesSpacesFixer From aa2853b43ffee9e32542ef7d2ecd44ca2837bdc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 02:14:36 +0200 Subject: [PATCH 16/36] Feat: Reconfigure TrailingCommaInMultilineFixer in accordance with PER2.0 (part of #94) --- ecs.php | 12 ++++----- .../Fixtures/Basic.correct.php.inc | 25 ++++++++++++++++++ .../Integration/Fixtures/Basic.wrong.php.inc | 26 +++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/ecs.php b/ecs.php index cc9cfce..be9da08 100644 --- a/ecs.php +++ b/ecs.php @@ -149,7 +149,6 @@ use SlevomatCodingStandard\Sniffs\ControlStructures\RequireNullSafeObjectOperatorSniff; use SlevomatCodingStandard\Sniffs\Exceptions\ReferenceThrowableOnlySniff; use SlevomatCodingStandard\Sniffs\Functions\RequireTrailingCommaInCallSniff; -use SlevomatCodingStandard\Sniffs\Functions\RequireTrailingCommaInDeclarationSniff; use SlevomatCodingStandard\Sniffs\TypeHints\ParameterTypeHintSniff; use SlevomatCodingStandard\Sniffs\TypeHints\PropertyTypeHintSniff; use SlevomatCodingStandard\Sniffs\TypeHints\ReturnTypeHintSniff; @@ -234,8 +233,6 @@ SingleLineEmptyBodyFixer::class, // Defined in PER 2.0 // Values separated by a comma on a single line should not have a trailing comma. NoTrailingCommaInSinglelineFixer::class, - // Multi-line arrays, arguments list and parameters list must have a trailing comma - TrailingCommaInMultilineFixer::class, // Arrays should be formatted like function/method arguments TrimArraySpacesFixer::class, // In array declaration, there MUST be a whitespace after each comma @@ -422,10 +419,8 @@ // > it may probably be a phpstan rule, more than cs rule - since it needs a class hierarchy to solve this // @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/6235 - // Multi-line arguments list in function/method declaration must have a trailing comma - RequireTrailingCommaInDeclarationSniff::class, // Multi-line arguments list in function/method call must have a trailing comma - RequireTrailingCommaInCallSniff::class, + RequireTrailingCommaInCallSniff::class, // TODO: will be redundant after https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/7989 is merged and released // Use `null-safe` operator `?->` where possible RequireNullSafeObjectOperatorSniff::class, @@ -552,6 +547,11 @@ FunctionDeclarationFixer::class, ['closure_fn_spacing' => 'none'], // Defined in PER 2.0 ) + // Multi-line arrays, arguments list and parameters list must have a trailing comma + ->withConfiguredRule( + TrailingCommaInMultilineFixer::class, + ['after_heredoc' => true, 'elements' => ['arguments', 'arrays', 'match', 'parameters']], // Defined in PER 2.0 + ) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 8ad27a2..8a5e1d8 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -110,4 +110,29 @@ class Basic public function emptyFunction2( $arg, ): void {} // SingleLineEmptyBodyFixer + + // TrailingCommaInMultilineFixer + public function multiline( + $arg1, + $arg2, + $arg3, + ): void { + // TrailingCommaInMultilineFixer + $isSet = isset( + $arg1, + $arg2, + $arg3, + ); + + $sprintf = sprintf( + '%s%s', + 'bar', + 'baz', + ); + + foo( + 1, + 2, + ); + } } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index ef49723..6a741fe 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -109,4 +109,30 @@ class Basic $arg ): void { } // SingleLineEmptyBodyFixer + + // TrailingCommaInMultilineFixer + public function multiline( + $arg1, + $arg2, + $arg3 + ): void + { + // TrailingCommaInMultilineFixer + $isSet = isset( + $arg1, + $arg2, + $arg3 + ); + + $sprintf = sprintf( + '%s%s', + 'bar', + 'baz' + ); + + foo( + 1, + 2 + ); + } } From 1b89496b30c588e5de9543ca2aa48b805f9c66a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 02:32:11 +0200 Subject: [PATCH 17/36] Docs: Add comments to various fixers --- ecs.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ecs.php b/ecs.php index be9da08..db7359d 100644 --- a/ecs.php +++ b/ecs.php @@ -285,9 +285,9 @@ NoUnreachableDefaultArgumentValueFixer::class, // There must be no `sprintf` calls with only the first argument. NoUselessSprintfFixer::class, - + // There must not be a space before colon in return type declarations. ReturnTypeDeclarationFixer::class, - + // Add `void` return type to functions with missing or empty return statements. VoidReturnFixer::class, // Remove leading slashes in `use` clauses. NoLeadingImportSlashFixer::class, @@ -307,15 +307,15 @@ CleanNamespaceFixer::class, // The namespace declaration line shouldn't contain leading whitespace. NoLeadingNamespaceWhitespaceFixer::class, - + // Binary operators should be surrounded by exactly one single space. BinaryOperatorSpacesFixer::class, // There must be no space around scope resolution double colons NoSpaceAroundDoubleColonFixer::class, - + // All instances created with new keyword must be followed by parentheses. NewWithParenthesesFixer::class, - + // There should not be space before or after object operators `->` and `?->`. ObjectOperatorWithoutWhitespaceFixer::class, - + // Replace all `<>` with `!=`. StandardizeNotEqualsFixer::class, // Standardize spaces around ternary operator. TernaryOperatorSpacesFixer::class, @@ -323,7 +323,7 @@ TernaryToElvisOperatorFixer::class, // Use `null` coalescing operator `??` where possible. TernaryToNullCoalescingFixer::class, - + // Unary operators should be placed adjacent (without a space) to their operands. UnaryOperatorSpacesFixer::class, NoBlankLinesAfterPhpdocFixer::class, From 6399ba0e72fd5892d91915da4708d402d8405a05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 11:31:07 +0200 Subject: [PATCH 18/36] Feat: Add FullyQualifiedStrictTypesFixer to import all FQCN (part of #94) --- ecs.php | 6 ++++++ tests/Integration/Fixtures/Basic.correct.php.inc | 8 ++++++-- tests/Integration/Fixtures/Basic.wrong.php.inc | 7 +++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/ecs.php b/ecs.php index db7359d..cad238d 100644 --- a/ecs.php +++ b/ecs.php @@ -79,6 +79,7 @@ use PhpCsFixer\Fixer\FunctionNotation\PhpdocToReturnTypeFixer; use PhpCsFixer\Fixer\FunctionNotation\ReturnTypeDeclarationFixer; use PhpCsFixer\Fixer\FunctionNotation\VoidReturnFixer; +use PhpCsFixer\Fixer\Import\FullyQualifiedStrictTypesFixer; use PhpCsFixer\Fixer\Import\NoLeadingImportSlashFixer; use PhpCsFixer\Fixer\Import\NoUnneededImportAliasFixer; use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer; @@ -552,6 +553,11 @@ TrailingCommaInMultilineFixer::class, ['after_heredoc' => true, 'elements' => ['arguments', 'arrays', 'match', 'parameters']], // Defined in PER 2.0 ) + // Removes the leading part of FQCN + ->withConfiguredRule( + FullyQualifiedStrictTypesFixer::class, + ['import_symbols' => true], // Also import symbols from other namespaces than in current file + ) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 8a5e1d8..1b59d94 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -1,8 +1,12 @@ Date: Tue, 7 May 2024 11:47:49 +0200 Subject: [PATCH 19/36] Feat: Add MultilineStringToHeredocFixer (part of #94) --- ecs.php | 3 +++ .../Integration/Fixtures/Basic.correct.php.inc | 17 +++++++++++++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 15 +++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/ecs.php b/ecs.php index cad238d..1346cb1 100644 --- a/ecs.php +++ b/ecs.php @@ -137,6 +137,7 @@ use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use PhpCsFixer\Fixer\Strict\StrictParamFixer; +use PhpCsFixer\Fixer\StringNotation\MultilineStringToHeredocFixer; use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer; use PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer; use PhpCsFixer\Fixer\Whitespace\BlankLineBeforeStatementFixer; @@ -386,6 +387,8 @@ StrictParamFixer::class, // Comparisons should be strict, `===` or `!==` must be used for comparisons StrictComparisonFixer::class, + // Convert multiline string to heredoc or nowdoc. + MultilineStringToHeredocFixer::class, // Convert double quotes to single quotes for simple strings SingleQuoteFixer::class, // Each element of an array must be indented exactly once. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 1b59d94..ad05bd6 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -63,6 +63,23 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully $baz = implode(',', ['foo', 'bar']); } + // HeredocIndentationFixer + $heredoc = << Date: Tue, 7 May 2024 12:02:11 +0200 Subject: [PATCH 20/36] Feat: Add LongToShorthandOperatorFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 6 ++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/ecs.php b/ecs.php index 1346cb1..08ea7af 100644 --- a/ecs.php +++ b/ecs.php @@ -93,6 +93,7 @@ use PhpCsFixer\Fixer\NamespaceNotation\NoLeadingNamespaceWhitespaceFixer; use PhpCsFixer\Fixer\Operator\BinaryOperatorSpacesFixer; use PhpCsFixer\Fixer\Operator\ConcatSpaceFixer; +use PhpCsFixer\Fixer\Operator\LongToShorthandOperatorFixer; use PhpCsFixer\Fixer\Operator\NewWithParenthesesFixer; use PhpCsFixer\Fixer\Operator\NoSpaceAroundDoubleColonFixer; use PhpCsFixer\Fixer\Operator\ObjectOperatorWithoutWhitespaceFixer; @@ -311,6 +312,8 @@ NoLeadingNamespaceWhitespaceFixer::class, // Binary operators should be surrounded by exactly one single space. BinaryOperatorSpacesFixer::class, + // Shorthand notation for operators should be used if possible. + LongToShorthandOperatorFixer::class, // There must be no space around scope resolution double colons NoSpaceAroundDoubleColonFixer::class, // All instances created with new keyword must be followed by parentheses. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index ad05bd6..19ad0be 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -63,6 +63,12 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully $baz = implode(',', ['foo', 'bar']); } + $i = 3; + $i += 6; // LongToShorthandOperatorFixer + $i *= 2; // LongToShorthandOperatorFixer + $text = 'foo'; + $text .= 'bar'; // LongToShorthandOperatorFixer + // HeredocIndentationFixer $heredoc = << Date: Tue, 7 May 2024 14:08:03 +0200 Subject: [PATCH 21/36] Feat: Add rules for attributes handling (AttributeEmptyParenthesesFixer, reconfigure MethodArgumentSpaceFixer and NoExtraBlankLinesFixer, DisallowAttributesJoiningSniff, DisallowMultipleAttributesPerLineSniff) (part of #94) --- composer.json | 4 ++-- ecs.php | 21 +++++++++++++++++-- .../Fixtures/NewPhpFeatures.correct.php.inc | 14 +++++++++++++ .../Fixtures/NewPhpFeatures.wrong.php.inc | 11 ++++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 2b6b219..2be57cf 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,8 @@ ], "require": { "php": "^8.0", - "friendsofphp/php-cs-fixer": "^3.0", - "slevomat/coding-standard": "^8.0", + "friendsofphp/php-cs-fixer": "^3.47.1", + "slevomat/coding-standard": "^8.6", "squizlabs/php_codesniffer": "^3.9", "symplify/easy-coding-standard": "^12.1.5" }, diff --git a/ecs.php b/ecs.php index 08ea7af..5d75f30 100644 --- a/ecs.php +++ b/ecs.php @@ -43,6 +43,7 @@ use PhpCsFixer\Fixer\ArrayNotation\NormalizeIndexBraceFixer; use PhpCsFixer\Fixer\ArrayNotation\TrimArraySpacesFixer; use PhpCsFixer\Fixer\ArrayNotation\WhitespaceAfterCommaInArrayFixer; +use PhpCsFixer\Fixer\AttributeNotation\AttributeEmptyParenthesesFixer; use PhpCsFixer\Fixer\Basic\BracesFixer; use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer; use PhpCsFixer\Fixer\Basic\PsrAutoloadingFixer; @@ -72,6 +73,7 @@ use PhpCsFixer\Fixer\FunctionNotation\FunctionDeclarationFixer; use PhpCsFixer\Fixer\FunctionNotation\ImplodeCallFixer; use PhpCsFixer\Fixer\FunctionNotation\LambdaNotUsedImportFixer; +use PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUnreachableDefaultArgumentValueFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUselessSprintfFixer; use PhpCsFixer\Fixer\FunctionNotation\PhpdocToParamTypeFixer; @@ -148,6 +150,8 @@ use PhpCsFixer\Fixer\Whitespace\NoWhitespaceInBlankLineFixer; use PhpCsFixer\Fixer\Whitespace\TypeDeclarationSpacesFixer; use PhpCsFixer\Fixer\Whitespace\TypesSpacesFixer; +use SlevomatCodingStandard\Sniffs\Attributes\DisallowAttributesJoiningSniff; +use SlevomatCodingStandard\Sniffs\Attributes\DisallowMultipleAttributesPerLineSniff; use SlevomatCodingStandard\Sniffs\Classes\RequireConstructorPropertyPromotionSniff; use SlevomatCodingStandard\Sniffs\ControlStructures\RequireNullSafeObjectOperatorSniff; use SlevomatCodingStandard\Sniffs\Exceptions\ReferenceThrowableOnlySniff; @@ -230,6 +234,8 @@ RandomApiMigrationFixer::class, // Cast shall be used, not `settype()` SetTypeToCastFixer::class, + // Attributes declared without arguments must not be followed by empty parentheses. + AttributeEmptyParenthesesFixer::class, // Array index should always be written by using square braces NormalizeIndexBraceFixer::class, // Empty body of class, interface, trait, enum or function must be abbreviated as {} and placed on the same line as the previous symbol, separated by a single space. @@ -415,6 +421,10 @@ // Takes `@return` annotation of non-mixed types and adjusts accordingly the function signature. PhpdocToReturnTypeFixer::class, ReturnTypeHintSniff::class, + // Requires that only one attribute can be placed inside #[]. + DisallowAttributesJoiningSniff::class, + // Disallows multiple attributes of some target on same line. + DisallowMultipleAttributesPerLineSniff::class, // Promote constructor properties // For php-cs-fixer implementation @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/5956 RequireConstructorPropertyPromotionSniff::class, @@ -520,13 +530,15 @@ // Removes extra blank lines and/or blank lines following configuration ->withConfiguredRule(NoExtraBlankLinesFixer::class, [ 'tokens' => [ - 'break', + 'attribute', + 'case', 'continue', 'curly_brace_block', + 'default', 'extra', 'parenthesis_brace_block', - 'return', 'square_brace_block', + 'switch', 'throw', 'use', 'use_trait', @@ -564,6 +576,11 @@ FullyQualifiedStrictTypesFixer::class, ['import_symbols' => true], // Also import symbols from other namespaces than in current file ) + // Spaces and newlines in method arguments and attributes + ->withConfiguredRule( + MethodArgumentSpaceFixer::class, + ['attribute_placement' => 'standalone', 'on_multiline' => 'ensure_fully_multiline'], + ) ->withSkip([ // We allow empty catch statements (but they must have comment - see EmptyCatchCommentSniff) EmptyStatementSniff::class . '.DetectedCatch' => null, diff --git a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc index 2b0cba1..bff038b 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc @@ -29,4 +29,18 @@ class NewPhpFeatures { return null; } + + // AttributeEmptyParenthesesFixer + #[SomeFunctionAttribute] + #[AnotherAttribute('bar')] + #[AnotherAttribute] + #[First] + #[Second] + public function functionWithAttributes( + // MethodArgumentSpaceFixer + #[ParamAttribute] + #[AnotherAttribute('foo')] + string $foo, + string $bar, + ): void {} } diff --git a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc index e3486bc..5629c7a 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc @@ -33,4 +33,15 @@ class NewPhpFeatures { return null; } + + // AttributeEmptyParenthesesFixer + #[SomeFunctionAttribute()] + #[AnotherAttribute('bar')]#[AnotherAttribute()] + + #[First, Second] + public function functionWithAttributes( + // MethodArgumentSpaceFixer + #[ParamAttribute] #[AnotherAttribute('foo')] string $foo, + string $bar, + ): void {} } From eb1479eb17f6ccd6c46f1d1f0cb1d9696d87b69c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 7 May 2024 15:16:57 +0200 Subject: [PATCH 22/36] Feat: Add NoUnneededControlParenthesesFixer fixer (part of #94) --- composer.json | 2 +- ecs.php | 6 ++++++ tests/Integration/Fixtures/Basic.correct.php.inc | 8 ++++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 8 ++++++++ 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2be57cf..168541a 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ "friendsofphp/php-cs-fixer": "^3.47.1", "slevomat/coding-standard": "^8.6", "squizlabs/php_codesniffer": "^3.9", - "symplify/easy-coding-standard": "^12.1.5" + "symplify/easy-coding-standard": "^12.1.9" }, "require-dev": { "ergebnis/composer-normalize": "^2.42.0", diff --git a/ecs.php b/ecs.php index 5d75f30..be2de95 100644 --- a/ecs.php +++ b/ecs.php @@ -63,6 +63,7 @@ use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer; use PhpCsFixer\Fixer\Comment\NoEmptyCommentFixer; use PhpCsFixer\Fixer\Comment\SingleLineCommentSpacingFixer; +use PhpCsFixer\Fixer\ControlStructure\NoUnneededControlParenthesesFixer; use PhpCsFixer\Fixer\ControlStructure\NoUselessElseFixer; use PhpCsFixer\Fixer\ControlStructure\SwitchContinueToBreakFixer; use PhpCsFixer\Fixer\ControlStructure\TrailingCommaInMultilineFixer; @@ -566,6 +567,11 @@ FunctionDeclarationFixer::class, ['closure_fn_spacing' => 'none'], // Defined in PER 2.0 ) + // Removes unneeded parentheses around specified control statements. + ->withConfiguredRule( + NoUnneededControlParenthesesFixer::class, + ['statements' => ['break', 'clone', 'continue', 'echo_print', 'others', 'switch_case', 'yield', 'yield_from']], + ) // Multi-line arrays, arguments list and parameters list must have a trailing comma ->withConfiguredRule( TrailingCommaInMultilineFixer::class, diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 19ad0be..9f2d726 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -69,6 +69,14 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully $text = 'foo'; $text .= 'bar'; // LongToShorthandOperatorFixer + switch ($i) { + case 1: // NoUnneededControlParenthesesFixer + $i++; + break; + default: + break; + } + // HeredocIndentationFixer $heredoc = << Date: Thu, 9 May 2024 18:00:37 +0200 Subject: [PATCH 23/36] Feat: Add NullableTypeDeclarationFixer to standardize nullable declaration using `?` (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 3 +++ tests/Integration/Fixtures/Basic.wrong.php.inc | 4 ++++ 3 files changed, 10 insertions(+) diff --git a/ecs.php b/ecs.php index be2de95..5cd61c7 100644 --- a/ecs.php +++ b/ecs.php @@ -89,6 +89,7 @@ use PhpCsFixer\Fixer\Import\OrderedImportsFixer; use PhpCsFixer\Fixer\LanguageConstruct\DeclareEqualNormalizeFixer; use PhpCsFixer\Fixer\LanguageConstruct\IsNullFixer; +use PhpCsFixer\Fixer\LanguageConstruct\NullableTypeDeclarationFixer; use PhpCsFixer\Fixer\LanguageConstruct\SingleSpaceAroundConstructFixer; use PhpCsFixer\Fixer\ListNotation\ListSyntaxFixer; use PhpCsFixer\Fixer\NamespaceNotation\BlankLinesBeforeNamespaceFixer; @@ -311,6 +312,8 @@ DeclareEqualNormalizeFixer::class, // Replaces `is_null($var)` expression with `null === $var` IsNullFixer::class, + // Nullable single type declaration should be standardised using question mark syntax. + NullableTypeDeclarationFixer::class, // Ensures a single space around language constructs. SingleSpaceAroundConstructFixer::class, // Namespace must not contain spacing, comments or PHPDoc. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 9f2d726..4ff5c04 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -170,4 +170,7 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully 2, ); } + + // NullableTypeDeclarationFixer + public function withParameters(?string $nullableValue): void {} } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index e54f569..39f1a87 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -167,4 +167,8 @@ but should be heredoc instead'; 2 ); } + + // NullableTypeDeclarationFixer + public function withParameters(null|string $nullableValue): void + {} } From d08cfc8a321f77b03401e5ba7685c267cf1d3948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Thu, 9 May 2024 18:25:05 +0200 Subject: [PATCH 24/36] Feat: Add NullableTypeDeclarationForDefaultNullValueFixer (part of #94) --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 8 ++++++-- tests/Integration/Fixtures/Basic.wrong.php.inc | 9 ++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ecs.php b/ecs.php index 5cd61c7..1045475 100644 --- a/ecs.php +++ b/ecs.php @@ -77,6 +77,7 @@ use PhpCsFixer\Fixer\FunctionNotation\MethodArgumentSpaceFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUnreachableDefaultArgumentValueFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUselessSprintfFixer; +use PhpCsFixer\Fixer\FunctionNotation\NullableTypeDeclarationForDefaultNullValueFixer; use PhpCsFixer\Fixer\FunctionNotation\PhpdocToParamTypeFixer; use PhpCsFixer\Fixer\FunctionNotation\PhpdocToPropertyTypeFixer; use PhpCsFixer\Fixer\FunctionNotation\PhpdocToReturnTypeFixer; @@ -296,6 +297,8 @@ NoUnreachableDefaultArgumentValueFixer::class, // There must be no `sprintf` calls with only the first argument. NoUselessSprintfFixer::class, + // Add `?` before single type declarations when parameters have a default null value. + NullableTypeDeclarationForDefaultNullValueFixer::class, // There must not be a space before colon in return type declarations. ReturnTypeDeclarationFixer::class, // Add `void` return type to functions with missing or empty return statements. diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 4ff5c04..53a39a6 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -171,6 +171,10 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully ); } - // NullableTypeDeclarationFixer - public function withParameters(?string $nullableValue): void {} + public function withParameters( + // NullableTypeDeclarationFixer + ?string $nullableValue, + // NullableTypeDeclarationForDefaultNullValueFixer + ?string $anotherNullableValue = null, + ): void {} } diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 39f1a87..441f93d 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -168,7 +168,10 @@ but should be heredoc instead'; ); } - // NullableTypeDeclarationFixer - public function withParameters(null|string $nullableValue): void - {} + public function withParameters( + // NullableTypeDeclarationFixer + null|string $nullableValue, + // NullableTypeDeclarationForDefaultNullValueFixer + string $anotherNullableValue = null, + ): void {} } From 84c9c7ef6aa54801b6e5197397c0db9b597518cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Fri, 10 May 2024 12:31:51 +0200 Subject: [PATCH 25/36] Test: Refactor integration tests to make them more versatile --- tests/Integration/CodingStandardTest.php | 52 ++++++++++++++++++------ 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/tests/Integration/CodingStandardTest.php b/tests/Integration/CodingStandardTest.php index 42cf1eb..316f58b 100644 --- a/tests/Integration/CodingStandardTest.php +++ b/tests/Integration/CodingStandardTest.php @@ -9,25 +9,23 @@ */ class CodingStandardTest extends TestCase { + private string $tempFixtureFile; + + /** @after */ + protected function cleanUpTempFixtureFile(): void + { + unlink($this->tempFixtureFile); + } + /** * @test * @dataProvider provideFilesToFix */ public function shouldFixFile(string $wrongFile, string $correctFile): void { - // copy wrongFile to a new temporary file - $fixtureFile = tempnam(sys_get_temp_dir(), 'ecs-test'); - if ($fixtureFile === false) { - $this->fail('Could not create temporary file'); - } - copy($wrongFile, $fixtureFile); - - shell_exec( - __DIR__ . '/../../vendor/bin/ecs check --no-progress-bar --no-ansi --no-interaction --fix ' . $fixtureFile, - ); + $fixedFile = $this->runEcsCheckOnFile($wrongFile); - $this->assertStringEqualsFile($correctFile, file_get_contents($fixtureFile)); - unlink($fixtureFile); + $this->assertStringEqualsFile($correctFile, file_get_contents($fixedFile)); } public function provideFilesToFix(): array @@ -40,4 +38,34 @@ public function provideFilesToFix(): array ], ]; } + + private function runEcsCheckOnFile(string $file): string + { + $fixtureFile = $this->initTempFixtureFile(); + + // copy source of wrongFile to a temporary file which will be modified by ECS + copy($file, $fixtureFile); + + shell_exec( + sprintf( + '%s/../../vendor/bin/ecs check --no-progress-bar --no-ansi --no-interaction --fix %s', + __DIR__, + $fixtureFile, + ), + ); + + return $fixtureFile; + } + + private function initTempFixtureFile(): string + { + // Create new file in temporary directory + $fixtureFile = tempnam(sys_get_temp_dir(), 'ecs-test'); + if ($fixtureFile === false) { + $this->fail('Could not create temporary file'); + } + $this->tempFixtureFile = $fixtureFile; // store to be able to remove it later + + return $fixtureFile; + } } From 5748e6c95fbbd52eb711e3cf2309f149d071805c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Fri, 10 May 2024 12:15:04 +0200 Subject: [PATCH 26/36] Feat: Add OctalNotationFixer for explicit octal notation using 0o in PHP 8.1+ (part of #94) --- ecs.php | 3 +++ tests/Integration/CodingStandardTest.php | 14 ++++++++++++++ tests/Integration/Fixtures/Php81.correct.php.inc | 12 ++++++++++++ tests/Integration/Fixtures/Php81.wrong.php.inc | 12 ++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 tests/Integration/Fixtures/Php81.correct.php.inc create mode 100644 tests/Integration/Fixtures/Php81.wrong.php.inc diff --git a/ecs.php b/ecs.php index 1045475..598caaf 100644 --- a/ecs.php +++ b/ecs.php @@ -46,6 +46,7 @@ use PhpCsFixer\Fixer\AttributeNotation\AttributeEmptyParenthesesFixer; use PhpCsFixer\Fixer\Basic\BracesFixer; use PhpCsFixer\Fixer\Basic\NoTrailingCommaInSinglelineFixer; +use PhpCsFixer\Fixer\Basic\OctalNotationFixer; use PhpCsFixer\Fixer\Basic\PsrAutoloadingFixer; use PhpCsFixer\Fixer\Basic\SingleLineEmptyBodyFixer; use PhpCsFixer\Fixer\Casing\ClassReferenceNameCasingFixer; @@ -245,6 +246,8 @@ SingleLineEmptyBodyFixer::class, // Defined in PER 2.0 // Values separated by a comma on a single line should not have a trailing comma. NoTrailingCommaInSinglelineFixer::class, + // Literal octal must be in 0o notation. + OctalNotationFixer::class, // Arrays should be formatted like function/method arguments TrimArraySpacesFixer::class, // In array declaration, there MUST be a whitespace after each comma diff --git a/tests/Integration/CodingStandardTest.php b/tests/Integration/CodingStandardTest.php index 316f58b..e8658a5 100644 --- a/tests/Integration/CodingStandardTest.php +++ b/tests/Integration/CodingStandardTest.php @@ -39,6 +39,20 @@ public function provideFilesToFix(): array ]; } + /** + * @test + * @requires PHP >= 8.1 + */ + public function shouldFixPhp81(): void + { + $fixedFile = $this->runEcsCheckOnFile(__DIR__ . '/Fixtures/Php81.wrong.php.inc'); + + $this->assertStringEqualsFile( + __DIR__ . '/Fixtures/Php81.correct.php.inc', + file_get_contents($fixedFile), + ); + } + private function runEcsCheckOnFile(string $file): string { $fixtureFile = $this->initTempFixtureFile(); diff --git a/tests/Integration/Fixtures/Php81.correct.php.inc b/tests/Integration/Fixtures/Php81.correct.php.inc new file mode 100644 index 0000000..f80047a --- /dev/null +++ b/tests/Integration/Fixtures/Php81.correct.php.inc @@ -0,0 +1,12 @@ + Date: Fri, 10 May 2024 13:03:50 +0200 Subject: [PATCH 27/36] Feat: Add SimpleToComplexStringVariableFixer for PHP 8.2+ (part of #94) --- ecs.php | 3 +++ tests/Integration/CodingStandardTest.php | 14 ++++++++++++++ tests/Integration/Fixtures/Php82.correct.php.inc | 12 ++++++++++++ tests/Integration/Fixtures/Php82.wrong.php.inc | 12 ++++++++++++ 4 files changed, 41 insertions(+) create mode 100644 tests/Integration/Fixtures/Php82.correct.php.inc create mode 100644 tests/Integration/Fixtures/Php82.wrong.php.inc diff --git a/ecs.php b/ecs.php index 598caaf..5a18aa5 100644 --- a/ecs.php +++ b/ecs.php @@ -145,6 +145,7 @@ use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use PhpCsFixer\Fixer\Strict\StrictParamFixer; use PhpCsFixer\Fixer\StringNotation\MultilineStringToHeredocFixer; +use PhpCsFixer\Fixer\StringNotation\SimpleToComplexStringVariableFixer; use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer; use PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer; use PhpCsFixer\Fixer\Whitespace\BlankLineBeforeStatementFixer; @@ -408,6 +409,8 @@ StrictComparisonFixer::class, // Convert multiline string to heredoc or nowdoc. MultilineStringToHeredocFixer::class, + // Converts explicit variables in double-quoted strings from simple to complex format (${ to {$). + SimpleToComplexStringVariableFixer::class, // Convert double quotes to single quotes for simple strings SingleQuoteFixer::class, // Each element of an array must be indented exactly once. diff --git a/tests/Integration/CodingStandardTest.php b/tests/Integration/CodingStandardTest.php index e8658a5..9417d5a 100644 --- a/tests/Integration/CodingStandardTest.php +++ b/tests/Integration/CodingStandardTest.php @@ -53,6 +53,20 @@ public function shouldFixPhp81(): void ); } + /** + * @test + * @requires PHP >= 8.2 + */ + public function shouldFixPhp82(): void + { + $fixedFile = $this->runEcsCheckOnFile(__DIR__ . '/Fixtures/Php82.wrong.php.inc'); + + $this->assertStringEqualsFile( + __DIR__ . '/Fixtures/Php82.correct.php.inc', + file_get_contents($fixedFile), + ); + } + private function runEcsCheckOnFile(string $file): string { $fixtureFile = $this->initTempFixtureFile(); diff --git a/tests/Integration/Fixtures/Php82.correct.php.inc b/tests/Integration/Fixtures/Php82.correct.php.inc new file mode 100644 index 0000000..6747ca0 --- /dev/null +++ b/tests/Integration/Fixtures/Php82.correct.php.inc @@ -0,0 +1,12 @@ + Date: Mon, 13 May 2024 12:05:00 +0200 Subject: [PATCH 28/36] Test: Add test for PhpDoc property fixer/sniff --- tests/Integration/CodingStandardTest.php | 1 + .../Fixtures/Basic.correct.php.inc | 13 --------- .../Integration/Fixtures/Basic.wrong.php.inc | 14 --------- .../Fixtures/PhpDoc.correct.php.inc | 25 ++++++++++++++++ .../Integration/Fixtures/PhpDoc.wrong.php.inc | 29 +++++++++++++++++++ 5 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 tests/Integration/Fixtures/PhpDoc.correct.php.inc create mode 100644 tests/Integration/Fixtures/PhpDoc.wrong.php.inc diff --git a/tests/Integration/CodingStandardTest.php b/tests/Integration/CodingStandardTest.php index 9417d5a..f0c8ac6 100644 --- a/tests/Integration/CodingStandardTest.php +++ b/tests/Integration/CodingStandardTest.php @@ -36,6 +36,7 @@ public function provideFilesToFix(): array __DIR__ . '/Fixtures/NewPhpFeatures.wrong.php.inc', __DIR__ . '/Fixtures/NewPhpFeatures.correct.php.inc', ], + 'PhpDoc' => [__DIR__ . '/Fixtures/PhpDoc.wrong.php.inc', __DIR__ . '/Fixtures/PhpDoc.correct.php.inc'], ]; } diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 53a39a6..52daa22 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -127,19 +127,6 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully ]; } - /** - * Very well documented method. - * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. - * @param int|float $second Second parameter does have a comment, unlike the first one. - * @param string|null $third Third parameter is optional and has a default value. - * @return mixed There is also information about return type. - * @throws \Exception - */ - public function veryWellDocumented(string $first, int|float $second, ?string $third = '3rd'): mixed - { - return $first . $third; - } - public function emptyFunction1(): void {} // SingleLineEmptyBodyFixer public function emptyFunction2( diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 441f93d..a468a82 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -120,20 +120,6 @@ but should be heredoc instead'; ]; } - /** - * Very well documented method. - * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. - * @param string $first - * @throws \Exception - * @param int|float $second Second parameter does have a comment, unlike the first one. - * @param string|null $third Third parameter is optional and has a default value. - * @return mixed There is also information about return type. - */ - public function veryWellDocumented(string $first, int|float $second, ?string $third = '3rd'): mixed - { - return $first . $third; - } - public function emptyFunction1(): void { } // SingleLineEmptyBodyFixer diff --git a/tests/Integration/Fixtures/PhpDoc.correct.php.inc b/tests/Integration/Fixtures/PhpDoc.correct.php.inc new file mode 100644 index 0000000..52595f9 --- /dev/null +++ b/tests/Integration/Fixtures/PhpDoc.correct.php.inc @@ -0,0 +1,25 @@ + Date: Mon, 13 May 2024 12:20:02 +0200 Subject: [PATCH 29/36] Test: Add test for PhpDoc param and return type hints fixers/sniffs --- ecs.php | 1 - .../Fixtures/PhpDoc.correct.php.inc | 13 ++++++++++++ .../Integration/Fixtures/PhpDoc.wrong.php.inc | 20 +++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/ecs.php b/ecs.php index 5a18aa5..431d83c 100644 --- a/ecs.php +++ b/ecs.php @@ -526,7 +526,6 @@ ->withConfiguredRule(ConcatSpaceFixer::class, ['spacing' => 'one']) // Removes `@param` and `@return` tags that don't provide any useful information ->withConfiguredRule(NoSuperfluousPhpdocTagsFixer::class, [ - 'allow_mixed' => true, // allow `@mixed` annotations to be preserved 'allow_unused_params' => false, // whether param annotation without actual signature is allowed 'remove_inheritdoc' => true, // remove @inheritDoc tags ]) diff --git a/tests/Integration/Fixtures/PhpDoc.correct.php.inc b/tests/Integration/Fixtures/PhpDoc.correct.php.inc index 52595f9..ce63f91 100644 --- a/tests/Integration/Fixtures/PhpDoc.correct.php.inc +++ b/tests/Integration/Fixtures/PhpDoc.correct.php.inc @@ -22,4 +22,17 @@ class PhpDoc { return $first . $third; } + + public function methodWithTypesInTypeHints(int $value, mixed $mixedType): bool + { + return $value > 3 ? true : false; + } + + /** + * @param string $stringParam This phpdoc should be preserved, because it contains some comment for $stringParam. + */ + public function methodWithMeaningfulParamComment(int $intParam, string $stringParam): void + { + // Do nothing. + } } diff --git a/tests/Integration/Fixtures/PhpDoc.wrong.php.inc b/tests/Integration/Fixtures/PhpDoc.wrong.php.inc index 84db958..1680520 100644 --- a/tests/Integration/Fixtures/PhpDoc.wrong.php.inc +++ b/tests/Integration/Fixtures/PhpDoc.wrong.php.inc @@ -26,4 +26,24 @@ class PhpDoc { return $first . $third; } + + /** + * @param int $value + * @param mixed $mixedType + * @return bool + */ + public function methodWithTypesInTypeHints($value, $mixedType) + { + return $value > 3 ? true : false; + } + + /** + * @param int $intParam + * @param string $stringParam This phpdoc should be preserved, because it contains some comment for $stringParam. + * @return void + */ + public function methodWithMeaningfulParamComment(int $intParam, string $stringParam): void + { + // Do nothing. + } } From 16c3cfc835c5968af44c17ac09b44541f876e65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 13 May 2024 15:35:25 +0200 Subject: [PATCH 30/36] Test: Add test for current phpunit fixers --- tests/Integration/CodingStandardTest.php | 1 + .../Fixtures/PhpUnit.correct.php.inc | 31 +++++++++++++++++++ .../Fixtures/PhpUnit.wrong.php.inc | 31 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 tests/Integration/Fixtures/PhpUnit.correct.php.inc create mode 100644 tests/Integration/Fixtures/PhpUnit.wrong.php.inc diff --git a/tests/Integration/CodingStandardTest.php b/tests/Integration/CodingStandardTest.php index f0c8ac6..4c92ccb 100644 --- a/tests/Integration/CodingStandardTest.php +++ b/tests/Integration/CodingStandardTest.php @@ -37,6 +37,7 @@ public function provideFilesToFix(): array __DIR__ . '/Fixtures/NewPhpFeatures.correct.php.inc', ], 'PhpDoc' => [__DIR__ . '/Fixtures/PhpDoc.wrong.php.inc', __DIR__ . '/Fixtures/PhpDoc.correct.php.inc'], + 'PhpUnit' => [__DIR__ . '/Fixtures/PhpUnit.wrong.php.inc', __DIR__ . '/Fixtures/PhpUnit.correct.php.inc'], ]; } diff --git a/tests/Integration/Fixtures/PhpUnit.correct.php.inc b/tests/Integration/Fixtures/PhpUnit.correct.php.inc new file mode 100644 index 0000000..bc52d30 --- /dev/null +++ b/tests/Integration/Fixtures/PhpUnit.correct.php.inc @@ -0,0 +1,31 @@ +assertTrue($data); + + // PhpUnitDedicateAssertFixer + $this->assertStringContainsString('o', 'foo'); + + $this->assertSame(1, 2); + // PhpUnitTestCaseStaticMethodCallsFixer + $this->assertSame(1, 2); + // PhpUnitTestCaseStaticMethodCallsFixer + $this->assertSame(1, 2); + } +} diff --git a/tests/Integration/Fixtures/PhpUnit.wrong.php.inc b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc new file mode 100644 index 0000000..510e4bb --- /dev/null +++ b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc @@ -0,0 +1,31 @@ +assertSame(true, $data); + + // PhpUnitDedicateAssertFixer + $this->assertTrue(str_contains('foo', 'o')); + + $this->assertSame(1, 2); + // PhpUnitTestCaseStaticMethodCallsFixer + self::assertSame(1, 2); + // PhpUnitTestCaseStaticMethodCallsFixer + static::assertSame(1, 2); + } +} From 0887cf3c4780381a521eb4cb27637755d2df95bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 13 May 2024 16:58:49 +0200 Subject: [PATCH 31/36] Feat: Add PhpUnitFqcnAnnotationFixer --- ecs.php | 3 +++ tests/Integration/Fixtures/PhpUnit.correct.php.inc | 3 +++ tests/Integration/Fixtures/PhpUnit.wrong.php.inc | 3 +++ 3 files changed, 9 insertions(+) diff --git a/ecs.php b/ecs.php index 431d83c..45d7ea3 100644 --- a/ecs.php +++ b/ecs.php @@ -132,6 +132,7 @@ use PhpCsFixer\Fixer\PhpUnit\PhpUnitDedicateAssertFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitDedicateAssertInternalTypeFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitExpectationFixer; +use PhpCsFixer\Fixer\PhpUnit\PhpUnitFqcnAnnotationFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitMockFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitMockShortWillReturnFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitNoExpectationAnnotationFixer; @@ -391,6 +392,8 @@ PhpUnitNoExpectationAnnotationFixer::class, // Usages of ->setExpectedException* methods MUST be replaced by ->expectException* methods PhpUnitExpectationFixer::class, + // PHPUnit annotations should be a FQCNs including a root namespace. + PhpUnitFqcnAnnotationFixer::class, // Visibility of setUp() and tearDown() method should be kept protected PhpUnitSetUpTearDownVisibilityFixer::class, // There should not be an empty `return` statement at the end of a function diff --git a/tests/Integration/Fixtures/PhpUnit.correct.php.inc b/tests/Integration/Fixtures/PhpUnit.correct.php.inc index bc52d30..46b3f92 100644 --- a/tests/Integration/Fixtures/PhpUnit.correct.php.inc +++ b/tests/Integration/Fixtures/PhpUnit.correct.php.inc @@ -4,6 +4,9 @@ namespace Lmc\CodingStandard\Integration\Fixtures; use PHPUnit\Framework\TestCase; +/** + * @covers \Lmc\CodingStandard\Integration\Fixtures\MyClass + */ class PhpUnit extends TestCase { // PhpUnitSetUpTearDownVisibilityFixer diff --git a/tests/Integration/Fixtures/PhpUnit.wrong.php.inc b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc index 510e4bb..a9aefd7 100644 --- a/tests/Integration/Fixtures/PhpUnit.wrong.php.inc +++ b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc @@ -4,6 +4,9 @@ namespace Lmc\CodingStandard\Integration\Fixtures; use PHPUnit\Framework\TestCase; +/** + * @covers Lmc\CodingStandard\Integration\Fixtures\MyClass + */ class PhpUnit extends TestCase { // PhpUnitSetUpTearDownVisibilityFixer From b1bfeb40ec08747fdfe866c29a803d1a4ac10352 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 13 May 2024 17:01:21 +0200 Subject: [PATCH 32/36] Feat: Add PhpUnitMethodCasingFixer --- ecs.php | 3 +++ tests/Integration/Fixtures/PhpUnit.correct.php.inc | 5 +++++ tests/Integration/Fixtures/PhpUnit.wrong.php.inc | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/ecs.php b/ecs.php index 45d7ea3..5f2c770 100644 --- a/ecs.php +++ b/ecs.php @@ -133,6 +133,7 @@ use PhpCsFixer\Fixer\PhpUnit\PhpUnitDedicateAssertInternalTypeFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitExpectationFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitFqcnAnnotationFixer; +use PhpCsFixer\Fixer\PhpUnit\PhpUnitMethodCasingFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitMockFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitMockShortWillReturnFixer; use PhpCsFixer\Fixer\PhpUnit\PhpUnitNoExpectationAnnotationFixer; @@ -538,6 +539,8 @@ ->withConfiguredRule(PhpdocOrderFixer::class, ['order' => ['param', 'return', 'throws']]) // Order phpdoc tags by value. ->withConfiguredRule(PhpdocOrderByValueFixer::class, ['annotations' => ['covers', 'group', 'throws']]) + // Enforce camel case for PHPUnit test methods. + ->withConfiguredRule(PhpUnitMethodCasingFixer::class, ['case' => 'camel_case']) // Calls to `PHPUnit\Framework\TestCase` static methods must all be of the same type (`$this->...`) ->withConfiguredRule(PhpUnitTestCaseStaticMethodCallsFixer::class, ['call_type' => 'this']) // An empty line feed must precede any configured statement diff --git a/tests/Integration/Fixtures/PhpUnit.correct.php.inc b/tests/Integration/Fixtures/PhpUnit.correct.php.inc index 46b3f92..642c49f 100644 --- a/tests/Integration/Fixtures/PhpUnit.correct.php.inc +++ b/tests/Integration/Fixtures/PhpUnit.correct.php.inc @@ -31,4 +31,9 @@ class PhpUnit extends TestCase // PhpUnitTestCaseStaticMethodCallsFixer $this->assertSame(1, 2); } + + public function testMethodName(): void // PhpUnitMethodCasingFixer + { + $this->assertTrue(true); + } } diff --git a/tests/Integration/Fixtures/PhpUnit.wrong.php.inc b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc index a9aefd7..16f0f6a 100644 --- a/tests/Integration/Fixtures/PhpUnit.wrong.php.inc +++ b/tests/Integration/Fixtures/PhpUnit.wrong.php.inc @@ -31,4 +31,9 @@ class PhpUnit extends TestCase // PhpUnitTestCaseStaticMethodCallsFixer static::assertSame(1, 2); } + + public function test_method_name(): void // PhpUnitMethodCasingFixer + { + $this->assertTrue(true); + } } From 409d729f8a676683387abe15d1595efb45de8549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Thu, 16 May 2024 14:54:52 +0200 Subject: [PATCH 33/36] Docs: Update comments - add missing and remove irrelevant --- ecs.php | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/ecs.php b/ecs.php index 5f2c770..9992cd7 100644 --- a/ecs.php +++ b/ecs.php @@ -349,9 +349,9 @@ TernaryToNullCoalescingFixer::class, // Unary operators should be placed adjacent (without a space) to their operands. UnaryOperatorSpacesFixer::class, - + // There should not be blank lines between docblock and the documented element. NoBlankLinesAfterPhpdocFixer::class, - + // There should not be empty PHPDoc blocks. NoEmptyPhpdocFixer::class, // PHPDoc should contain `@param` for all params. PhpdocAddMissingParamAnnotationFixer::class, @@ -445,17 +445,8 @@ // Promote constructor properties // For php-cs-fixer implementation @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/5956 RequireConstructorPropertyPromotionSniff::class, - - // switch -> match - // @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/5894 - - // Require \Stringable interface in classes implementing __toString() method - // > it may probably be a phpstan rule, more than cs rule - since it needs a class hierarchy to solve this - // @see https://github.com/FriendsOfPHP/PHP-CS-Fixer/issues/6235 - // Multi-line arguments list in function/method call must have a trailing comma RequireTrailingCommaInCallSniff::class, // TODO: will be redundant after https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/pull/7989 is merged and released - // Use `null-safe` operator `?->` where possible RequireNullSafeObjectOperatorSniff::class, ], From 8a352961b6081bb6040e3e4a2c0a40d0f84bfa8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 21 May 2024 14:17:05 +0200 Subject: [PATCH 34/36] Add MagicMethodCasingFixer to check correct magic method casing --- ecs.php | 3 +++ tests/Integration/Fixtures/Basic.correct.php.inc | 8 ++++++++ tests/Integration/Fixtures/Basic.wrong.php.inc | 8 ++++++++ 3 files changed, 19 insertions(+) diff --git a/ecs.php b/ecs.php index 9992cd7..44dde8a 100644 --- a/ecs.php +++ b/ecs.php @@ -50,6 +50,7 @@ use PhpCsFixer\Fixer\Basic\PsrAutoloadingFixer; use PhpCsFixer\Fixer\Basic\SingleLineEmptyBodyFixer; use PhpCsFixer\Fixer\Casing\ClassReferenceNameCasingFixer; +use PhpCsFixer\Fixer\Casing\MagicConstantCasingFixer; use PhpCsFixer\Fixer\Casing\MagicMethodCasingFixer; use PhpCsFixer\Fixer\Casing\NativeFunctionCasingFixer; use PhpCsFixer\Fixer\Casing\NativeTypeDeclarationCasingFixer; @@ -259,6 +260,8 @@ PsrAutoloadingFixer::class, // When referencing an internal class it must be written using the correct casing. ClassReferenceNameCasingFixer::class, + // Magic constants should be referred to using the correct casing. + MagicConstantCasingFixer::class, // Magic method definitions and calls must be using the correct casing MagicMethodCasingFixer::class, // Function defined by PHP should be called using the correct casing diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index 52daa22..b83d449 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -15,6 +15,12 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully protected int $myProperty = 666; // OrderedClassElementsFixer + // MagicMethodCasingFixer, OrderedClassElementsFixer + public function __toString(): string + { + return ''; + } + public function isEqual($a, ?string $b): ?bool // VisibilityRequiredFixer, CompactNullableTypeDeclarationFixer { // TrimArraySpacesFixer @@ -46,6 +52,8 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully public function fooBar(mixed $foo): mixed // FunctionDeclarationFixer { + // MagicConstantCasingFixer + $magicConstant = __DIR__; $value = 5; // FunctionDeclarationFixer $function = function ($foo) use ($value) { diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index a468a82..e6753e8 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -38,6 +38,8 @@ class Basic extends \Some\Other\Namespace\AbstractBasic implements \Lmc\CodingSt public function fooBar ( mixed $foo ): mixed // FunctionDeclarationFixer { + // MagicConstantCasingFixer + $magicConstant = __dir__; $value = 5; // FunctionDeclarationFixer $function = function($foo)use($value) { @@ -160,4 +162,10 @@ but should be heredoc instead'; // NullableTypeDeclarationForDefaultNullValueFixer string $anotherNullableValue = null, ): void {} + + // MagicMethodCasingFixer, OrderedClassElementsFixer + public function __ToString(): string + { + return ''; + } } From 117d879f48291f395f3f3f5d0729ae9b9476cc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Tue, 21 May 2024 14:33:25 +0200 Subject: [PATCH 35/36] Add PhpdocTrimConsecutiveBlankLineSeparationFixer to removes extra blank lines in PHPDoc. --- ecs.php | 3 +++ tests/Integration/Fixtures/PhpDoc.correct.php.inc | 4 +++- tests/Integration/Fixtures/PhpDoc.wrong.php.inc | 6 +++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ecs.php b/ecs.php index 44dde8a..32a21e1 100644 --- a/ecs.php +++ b/ecs.php @@ -124,6 +124,7 @@ use PhpCsFixer\Fixer\Phpdoc\PhpdocScalarFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocSingleLineVarSpacingFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocToCommentFixer; +use PhpCsFixer\Fixer\Phpdoc\PhpdocTrimConsecutiveBlankLineSeparationFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocTrimFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocTypesFixer; use PhpCsFixer\Fixer\Phpdoc\PhpdocVarAnnotationCorrectOrderFixer; @@ -372,6 +373,8 @@ PhpdocScalarFixer::class, // Single line `@var` PHPDoc should have proper spacing. PhpdocSingleLineVarSpacingFixer::class, + // Removes extra blank lines after summary and after description in PHPDoc. + PhpdocTrimConsecutiveBlankLineSeparationFixer::class, // PHPDoc should start and end with content PhpdocTrimFixer::class, // Docblocks should only be used on structural elements. diff --git a/tests/Integration/Fixtures/PhpDoc.correct.php.inc b/tests/Integration/Fixtures/PhpDoc.correct.php.inc index ce63f91..b680816 100644 --- a/tests/Integration/Fixtures/PhpDoc.correct.php.inc +++ b/tests/Integration/Fixtures/PhpDoc.correct.php.inc @@ -12,7 +12,9 @@ class PhpDoc /** * Very well documented method. - * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. + * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer, PhpdocTrimConsecutiveBlankLineSeparationFixer + * and possibly other Phpdoc rules. + * * @param int|float $second Second parameter does have a comment, unlike the first one. * @param string|null $third Third parameter is optional and has a default value. * @return mixed There is also information about return type. diff --git a/tests/Integration/Fixtures/PhpDoc.wrong.php.inc b/tests/Integration/Fixtures/PhpDoc.wrong.php.inc index 1680520..a13aede 100644 --- a/tests/Integration/Fixtures/PhpDoc.wrong.php.inc +++ b/tests/Integration/Fixtures/PhpDoc.wrong.php.inc @@ -15,7 +15,11 @@ class PhpDoc /** * Very well documented method. - * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer and possibly other Phpdoc rules. + * It tests PhpdocAlignFixer, NoSuperfluousPhpdocTagsFixer, PhpdocTrimConsecutiveBlankLineSeparationFixer + * and possibly other Phpdoc rules. + * + * + * * @param string $first * @throws \Exception * @param int|float $second Second parameter does have a comment, unlike the first one. From a2dfe957d1ac2e6c275fb1116f255e7ef4272ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Thu, 23 May 2024 15:11:24 +0200 Subject: [PATCH 36/36] Feat: Remove SingleLineCommentSpacingFixer and MultilineStringToHeredocFixer from default checks, keep them suggested in README --- README.md | 4 +++- UPGRADE-4.0.md | 5 +++++ ecs.php | 6 ------ tests/Integration/Fixtures/Basic.correct.php.inc | 10 ---------- tests/Integration/Fixtures/Basic.wrong.php.inc | 8 -------- 5 files changed, 8 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 17a6a7b..ead7009 100644 --- a/README.md +++ b/README.md @@ -82,8 +82,10 @@ return ECSConfig::configure() [ // PHPUnit attributes must be used over their respective PHPDoc-based annotations. (Use with PHPUnit 10+.) PhpUnitAttributesFixer::class, - // Single-line comments must have proper spacing. + // Single-line comments must have proper spacing (one space after `//`). SingleLineCommentSpacingFixer::class, + // Convert multiline string to heredoc or nowdoc. + MultilineStringToHeredocFixer::class, ] ) // Enforce line-length to 120 characters. diff --git a/UPGRADE-4.0.md b/UPGRADE-4.0.md index e614b51..a0a4cf4 100644 --- a/UPGRADE-4.0.md +++ b/UPGRADE-4.0.md @@ -85,6 +85,11 @@ $ vendor/bin/ecs check --ansi src/ tests/ # old $ vendor/bin/ecs check --ansi # new ``` +### 6. Add some optional rules +On top of default rules included in ecs.php, there are some more opinionated ones you may want to add. + +These suggested rules are listed in [README.md](https://github.com/lmc-eu/php-coding-standard?tab=readme-ov-file#add-custom-checks-or-override-default-settings). + ### 5. BE CAREFUL WITH SUGGESTED CHANGES! ⚠️ Some of the new default fixers introduced in php-coding-standard 4.0 and 4.1 suggest changes, which - if not diff --git a/ecs.php b/ecs.php index 32a21e1..60c8950 100644 --- a/ecs.php +++ b/ecs.php @@ -64,7 +64,6 @@ use PhpCsFixer\Fixer\ClassNotation\SingleTraitInsertPerStatementFixer; use PhpCsFixer\Fixer\ClassNotation\VisibilityRequiredFixer; use PhpCsFixer\Fixer\Comment\NoEmptyCommentFixer; -use PhpCsFixer\Fixer\Comment\SingleLineCommentSpacingFixer; use PhpCsFixer\Fixer\ControlStructure\NoUnneededControlParenthesesFixer; use PhpCsFixer\Fixer\ControlStructure\NoUselessElseFixer; use PhpCsFixer\Fixer\ControlStructure\SwitchContinueToBreakFixer; @@ -148,7 +147,6 @@ use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; use PhpCsFixer\Fixer\Strict\StrictComparisonFixer; use PhpCsFixer\Fixer\Strict\StrictParamFixer; -use PhpCsFixer\Fixer\StringNotation\MultilineStringToHeredocFixer; use PhpCsFixer\Fixer\StringNotation\SimpleToComplexStringVariableFixer; use PhpCsFixer\Fixer\StringNotation\SingleQuoteFixer; use PhpCsFixer\Fixer\Whitespace\ArrayIndentationFixer; @@ -283,8 +281,6 @@ SingleTraitInsertPerStatementFixer::class, // There should not be any empty comments NoEmptyCommentFixer::class, - // Single-line comments must have proper spacing. - SingleLineCommentSpacingFixer::class, // There should not be useless `else` cases NoUselessElseFixer::class, // Switch case must not be ended with `continue` but with `break`. @@ -417,8 +413,6 @@ StrictParamFixer::class, // Comparisons should be strict, `===` or `!==` must be used for comparisons StrictComparisonFixer::class, - // Convert multiline string to heredoc or nowdoc. - MultilineStringToHeredocFixer::class, // Converts explicit variables in double-quoted strings from simple to complex format (${ to {$). SimpleToComplexStringVariableFixer::class, // Convert double quotes to single quotes for simple strings diff --git a/tests/Integration/Fixtures/Basic.correct.php.inc b/tests/Integration/Fixtures/Basic.correct.php.inc index b83d449..29bbad2 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -95,16 +95,6 @@ class Basic extends AbstractBasic implements InterfaceFromThisNamespace // Fully $newdoc = <<<'NEWDOC' This is a $newdoc, where variables are not expanded. NEWDOC; - // MultilineStringToHeredocFixer - $multilineString = <<<'EOD' - This string - spans multiple lines - but should be heredoc instead - EOD; - - // SingleLineCommentSpacingFixer - // This comment should have space on the beginning - /* So should this one, also with space on the end */ // TernaryToElvisOperatorFixer return ($foo ?: 'not true'); diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index e6753e8..2fd69d8 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -81,14 +81,6 @@ HEREDOC; $newdoc = <<<'NEWDOC' This is a $newdoc, where variables are not expanded. NEWDOC; - // MultilineStringToHeredocFixer - $multilineString = 'This string -spans multiple lines -but should be heredoc instead'; - - // SingleLineCommentSpacingFixer - //This comment should have space on the beginning - /*So should this one, also with space on the end*/ // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true');