From 5a5d6fd083366ef60fc2176d01eaa7d88a4807d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 13 May 2024 17:55:03 +0200 Subject: [PATCH 01/37] Feat: Re-add fixers for property/parameter/return types and constructor property promotion --- ecs.php | 31 +++++++++++++++++++ .../Fixtures/NewPhpFeatures.correct.php.inc | 5 +-- .../Fixtures/NewPhpFeatures.wrong.php.inc | 2 +- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/ecs.php b/ecs.php index 5d49511..f8723da 100644 --- a/ecs.php +++ b/ecs.php @@ -69,6 +69,9 @@ use PhpCsFixer\Fixer\FunctionNotation\LambdaNotUsedImportFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUnreachableDefaultArgumentValueFixer; use PhpCsFixer\Fixer\FunctionNotation\NoUselessSprintfFixer; +use PhpCsFixer\Fixer\FunctionNotation\PhpdocToParamTypeFixer; +use PhpCsFixer\Fixer\FunctionNotation\PhpdocToPropertyTypeFixer; +use PhpCsFixer\Fixer\FunctionNotation\PhpdocToReturnTypeFixer; use PhpCsFixer\Fixer\FunctionNotation\ReturnTypeDeclarationFixer; use PhpCsFixer\Fixer\FunctionNotation\VoidReturnFixer; use PhpCsFixer\Fixer\Import\NoLeadingImportSlashFixer; @@ -130,10 +133,14 @@ use PhpCsFixer\Fixer\Whitespace\NoExtraBlankLinesFixer; use PhpCsFixer\Fixer\Whitespace\NoWhitespaceInBlankLineFixer; use PhpCsFixer\Fixer\Whitespace\TypeDeclarationSpacesFixer; +use SlevomatCodingStandard\Sniffs\Classes\RequireConstructorPropertyPromotionSniff; 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; use SlevomatCodingStandard\Sniffs\TypeHints\UnionTypeHintFormatSniff; use Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer; use Symplify\EasyCodingStandard\Config\ECSConfig; @@ -370,6 +377,18 @@ ReferenceThrowableOnlySniff::class, // The @param, @return, @var and inline @var annotations should keep standard format ParamReturnAndVarTagMalformsFixer::class, + // Takes `@var` annotation of non-mixed types and adjusts accordingly the property signature to a native PHP 7.4+ type-hint. + PhpdocToPropertyTypeFixer::class, + PropertyTypeHintSniff::class, + // Takes `@param` annotations of non-mixed types and adjusts accordingly the function signature to a native type-hints. + PhpdocToParamTypeFixer::class, + ParameterTypeHintSniff::class, + // Takes `@return` annotation of non-mixed types and adjusts accordingly the function signature. + PhpdocToReturnTypeFixer::class, + ReturnTypeHintSniff::class, + // 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 @@ -511,6 +530,18 @@ // Allow single line closures ScopeClosingBraceSniff::class . '.ContentBefore' => null, + // Skip unwanted rules from PropertyTypeHintSniff + PropertyTypeHintSniff::class . '.' . PropertyTypeHintSniff::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION => null, + PropertyTypeHintSniff::class . '.' . PropertyTypeHintSniff::CODE_MISSING_ANY_TYPE_HINT => null, + + // Skip unwanted rules from ParameterTypeHintSniff + ParameterTypeHintSniff::class . '.' . ParameterTypeHintSniff::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION => null, + ParameterTypeHintSniff::class . '.' . ParameterTypeHintSniff::CODE_MISSING_ANY_TYPE_HINT => null, + + // Skip unwanted rules from ReturnTypeHintSniff + ReturnTypeHintSniff::class . '.' . ReturnTypeHintSniff::CODE_MISSING_TRAVERSABLE_TYPE_HINT_SPECIFICATION => null, + ReturnTypeHintSniff::class . '.' . ReturnTypeHintSniff::CODE_MISSING_ANY_TYPE_HINT => null, + // We use declare(strict_types=1); after opening tag BlankLineAfterOpeningTagFixer::class => null, ]); diff --git a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc index f2962dd..c6e2726 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.correct.php.inc @@ -4,11 +4,8 @@ namespace Lmc\CodingStandard\Integration\Fixtures; class NewPhpFeatures { - private string $someString; - - public function __construct(string $someString) + public function __construct(private string $someString) // RequireConstructorPropertyPromotionSniff { - $this->someString = $someString; } public function php80features( diff --git a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc index 3435f7e..49d1d08 100644 --- a/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc +++ b/tests/Integration/Fixtures/NewPhpFeatures.wrong.php.inc @@ -6,7 +6,7 @@ class NewPhpFeatures { private string $someString; - public function __construct(string $someString) + public function __construct(string $someString) // RequireConstructorPropertyPromotionSniff { $this->someString = $someString; } From e7ff98dfe923cdd6465a9b8ecf7688f96ea64afe 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 02/37] 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 ac83458..e4fa5ee 100644 --- a/ecs-internal.php +++ b/ecs-internal.php @@ -5,7 +5,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 f8723da..8a142ca 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; @@ -331,6 +332,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 922a41a..852218e 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -24,6 +24,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 aa795ae..ca4ca1c 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -20,6 +20,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 90a6a3054a13b4b5149409f948b7304e075d9834 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 03/37] 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 8a142ca..a3a2766 100644 --- a/ecs.php +++ b/ecs.php @@ -214,8 +214,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()` @@ -455,6 +453,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 852218e..b5a606e 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -29,8 +29,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 ca4ca1c..bef10c3 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -25,8 +25,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 9450224f44051e9512e337885e64d9f9b51c84ce 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 04/37] 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 a3a2766..4aea254 100644 --- a/ecs.php +++ b/ecs.php @@ -134,6 +134,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; @@ -142,7 +143,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; @@ -262,6 +262,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. @@ -502,8 +504,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 2367d7827cd0a4ffcb7abe4b4873dc5c988e4ad3 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 05/37] 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 5d7d2e60d3f17c43a53cd283b0579d445e835426 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 06/37] 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 4aea254..9e80f8b 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; @@ -294,6 +295,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 b5a606e..2119c3b 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']; @@ -14,6 +14,8 @@ class Basic $lambdaWithUnusedImport = function () { return 'foo'; }; // NoUselessSprintfFixer $uselessSprintf = 'bar'; + // NoSpaceAroundDoubleColonFixer + $className = DateTime::class; // SingleSpaceAfterConstructFixer if ($a == $b) { return true; diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index bef10c3..3b21f8a 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']; @@ -13,6 +13,8 @@ class Basic $lambdaWithUnusedImport = function () use ($fooBar) { return 'foo'; }; // NoUselessSprintfFixer $uselessSprintf = sprintf('bar'); + // NoSpaceAroundDoubleColonFixer + $className = DateTime :: class; // SingleSpaceAfterConstructFixer if ($a == $b) { return true; } return false; // BlankLineBeforeStatementFixer From e2fc584cdcf6526a715815658b5b427413175225 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 07/37] 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 9e80f8b..f882ef3 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; @@ -231,6 +232,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 2119c3b..72d10ce 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -16,6 +16,8 @@ class Basic $uselessSprintf = 'bar'; // NoSpaceAroundDoubleColonFixer $className = DateTime::class; + // ClassReferenceNameCasingFixer + $date = new \DateTime(); // SingleSpaceAfterConstructFixer if ($a == $b) { return true; diff --git a/tests/Integration/Fixtures/Basic.wrong.php.inc b/tests/Integration/Fixtures/Basic.wrong.php.inc index 3b21f8a..e566cfa 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -15,6 +15,8 @@ class Basic $uselessSprintf = sprintf('bar'); // NoSpaceAroundDoubleColonFixer $className = DateTime :: class; + // ClassReferenceNameCasingFixer + $date = new \datetime(); // SingleSpaceAfterConstructFixer if ($a == $b) { return true; } return false; // BlankLineBeforeStatementFixer From 29be507ad1f903f5442be315bfe73eb6dcc814d3 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 08/37] 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 f882ef3..738e80b 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; @@ -280,13 +281,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 72d10ce..8001e97 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 09/37] 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 738e80b..da287e2 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; @@ -255,6 +256,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 8001e97..08d762b 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -42,6 +42,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 1fb863d..db6a35e 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -38,6 +38,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 2d47c2f9acc8864de2fbe667ea89cc76be100887 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 10/37] 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 da287e2..37bd1b1 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; @@ -516,6 +517,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 08d762b..a3fa5c2 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 db6a35e..3b0c3c1 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -27,6 +27,8 @@ class Basic return false; // BlankLineBeforeStatementFixer } + public const MY_PUBLIC_CONST = 333; // OrderedClassElementsFixer + public function fooBar(mixed $foo): mixed { // PhpdocToCommentFixer @@ -45,4 +47,8 @@ class Basic // TernaryToElvisOperatorFixer return ($foo ? $foo : 'not true'); } + + protected int $myProperty = 666; // OrderedClassElementsFixer + + use SomeUsefulTrait; // OrderedClassElementsFixer } From f8a713ab3042378171d91f6843a994e204c89ea0 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 11/37] 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 37bd1b1..ec99fd0 100644 --- a/ecs.php +++ b/ecs.php @@ -225,7 +225,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 a3fa5c2..dd012d4 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 @@ -54,4 +56,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 3b0c3c1..077e07c 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 @@ -51,4 +53,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 5c3498cf16f0dd913856a9af031315276b542d84 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 12/37] 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 ec99fd0..3aa309b 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; @@ -496,6 +497,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 dd012d4..c575d1f 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -75,4 +75,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 077e07c..f0909aa 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -72,4 +72,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 158ccbd1ca942b51d72c30e0d3ec7db4d5904a1a 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 13/37] 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 3aa309b..2c165f6 100644 --- a/ecs.php +++ b/ecs.php @@ -336,8 +336,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. @@ -499,6 +497,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 c575d1f..52131b0 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -81,8 +81,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 6f6f49ea9307d25814eef29049ea84cc575f70fa 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 14/37] 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 2c165f6..d145e52 100644 --- a/ecs.php +++ b/ecs.php @@ -134,6 +134,7 @@ use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; 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; @@ -382,6 +383,8 @@ StrictParamFixer::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 52131b0..8d38f7e 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -64,12 +64,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 f0909aa..7af2533 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -61,12 +61,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 7369bfabda85a2cd72b43d03343fcb664c572bbe 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 15/37] 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 d145e52..e852820 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; @@ -540,6 +541,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 8d38f7e..ce06904 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -38,8 +38,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 7af2533..974772b 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -31,8 +31,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 9f9bb181bcd735ec7edcc88f754b9bf63a1c005d 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 16/37] 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 e852820..b9ce6c2 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; @@ -228,6 +229,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 ce06904..0e16b68 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -102,4 +102,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 974772b..ad787b1 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -99,4 +99,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 dda2b59700cd9f26464b5d12e1cf1a43d4733301 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 17/37] 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 b9ce6c2..7c9b1b1 100644 --- a/ecs.php +++ b/ecs.php @@ -148,7 +148,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; @@ -233,8 +232,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 @@ -419,10 +416,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, @@ -549,6 +544,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 0e16b68..5687552 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -108,4 +108,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 ad787b1..5a8f985 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -107,4 +107,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 5efeb00951017772b5241fb36c0e081cc548d16f 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 18/37] 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 7c9b1b1..f65a390 100644 --- a/ecs.php +++ b/ecs.php @@ -284,9 +284,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, @@ -306,15 +306,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, @@ -322,7 +322,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 601faedeadf783467705f0d58bbd41b423d1a5b3 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 19/37] 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 f65a390..3dd976a 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; @@ -549,6 +550,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 5687552..2f83818 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 20/37] 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 3dd976a..b99cb0b 100644 --- a/ecs.php +++ b/ecs.php @@ -136,6 +136,7 @@ use PhpCsFixer\Fixer\Semicolon\SpaceAfterSemicolonFixer; use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; 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; @@ -383,6 +384,8 @@ DeclareStrictTypesFixer::class, // Functions should be used with `$strict` param set to `true` StrictParamFixer::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 2f83818..e936f56 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -61,6 +61,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 21/37] 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 b99cb0b..a035558 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; @@ -310,6 +311,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 e936f56..492fe38 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -61,6 +61,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 22/37] 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 e328c91..7cdbc3d 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 a035558..0760db8 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; @@ -147,6 +149,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; @@ -229,6 +233,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. @@ -412,6 +418,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, @@ -517,13 +527,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', @@ -561,6 +573,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 d6bbd2cb7fd3c7c23d304395bdac461f7981cc99 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 23/37] 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 7cdbc3d..b785d32 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.13.2", diff --git a/ecs.php b/ecs.php index 0760db8..d87041b 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; @@ -563,6 +564,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 492fe38..f94ea3d 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -67,6 +67,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 24/37] 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 d87041b..ce8af51 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; @@ -310,6 +311,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 f94ea3d..e714ccc 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -168,4 +168,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 a140cf5..8cd6609 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -165,4 +165,8 @@ but should be heredoc instead'; 2 ); } + + // NullableTypeDeclarationFixer + public function withParameters(null|string $nullableValue): void + {} } From ab8cd81b46b7266283fe101ae7daee86f837f8f2 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 25/37] 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 ce8af51..8d06383 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; @@ -295,6 +296,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 e714ccc..ae519cf 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -169,6 +169,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 8cd6609..676a508 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -166,7 +166,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 e55a5180ac9b756db8ecfbb59b7df9d78970c8d1 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 26/37] 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 2a3ccec97f6bf51ac0d801d8ff2d82bb3c159575 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 27/37] 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 8d06383..cb306b1 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; @@ -244,6 +245,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 28/37] 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 cb306b1..744d956 100644 --- a/ecs.php +++ b/ecs.php @@ -144,6 +144,7 @@ use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer; 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; @@ -405,6 +406,8 @@ StrictParamFixer::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 29/37] 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 ae519cf..d43a1d3 100644 --- a/tests/Integration/Fixtures/Basic.correct.php.inc +++ b/tests/Integration/Fixtures/Basic.correct.php.inc @@ -125,19 +125,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 676a508..c190038 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -118,20 +118,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 30/37] 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 744d956..3055939 100644 --- a/ecs.php +++ b/ecs.php @@ -523,7 +523,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 4be6d5ce35bace1dcec6880f5136364dd111d4e3 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 31/37] 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 1f5454bd4d66ed7ed58671ad00eb19e9f764f969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Machulda?= Date: Mon, 13 May 2024 16:58:16 +0200 Subject: [PATCH 32/37] Docs: Mention PhpUnitAttributesFixer in README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 62e17be..469db44 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,12 @@ return ECSConfig::configure() __DIR__ . '/vendor/lmc/coding-standard/ecs.php', ] ) + ->withRules( + [ + // PHPUnit attributes must be used over their respective PHPDoc-based annotations. (Use with PHPUnit 10+.) + PhpUnitAttributesFixer::class, + ] + ) // Enforce line-length to 120 characters ->withConfiguredRule(LineLengthSniff::class, ['absoluteLineLimit' => 120]) // Tests must have @test annotation From 3f2a451f109784d569e233e6e76c666243b891e3 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 33/37] 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 3055939..89701b0 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; @@ -390,6 +391,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 815901dfbea6d96c8d376387d965f293c69fdf57 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 34/37] 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 89701b0..9931a42 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; @@ -535,6 +536,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 b660c087edb8efc1d4935ac8ba2c023c8c0c3ff0 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 35/37] 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 9931a42..07aef91 100644 --- a/ecs.php +++ b/ecs.php @@ -348,9 +348,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, @@ -442,17 +442,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 0f8e5419a1e11c4f2e7c5a65db930f962188f79a 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 36/37] 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 07aef91..4f7784f 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; @@ -258,6 +259,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 d43a1d3..b2e6454 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 @@ -44,6 +50,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 c190038..15bd67a 100644 --- a/tests/Integration/Fixtures/Basic.wrong.php.inc +++ b/tests/Integration/Fixtures/Basic.wrong.php.inc @@ -36,6 +36,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) { @@ -158,4 +160,10 @@ but should be heredoc instead'; // NullableTypeDeclarationForDefaultNullValueFixer string $anotherNullableValue = null, ): void {} + + // MagicMethodCasingFixer, OrderedClassElementsFixer + public function __ToString(): string + { + return ''; + } } From f83f1916ffaa53b5d680a0f8b62836123c65ca8b 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 37/37] 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 4f7784f..60049c6 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; @@ -371,6 +372,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.