From d39c27c8b0178bce87ddd4e9c730cfecc166bb31 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 18 Aug 2020 11:10:39 +0200 Subject: [PATCH 1/4] [CodingStandard] Add anntotation new-line indent rule --- .../src/Exception/EdgeFindingException.php | 11 + .../Exception/NotImplementedYetException.php | 11 + .../NewlineInNestedAnnotationFixer.php | 91 ++++++++ .../FixerAnalyzer/DoctrineBlockFinder.php | 203 ++++++++++++++++++ .../Fixture/basic_annotation_join.php.inc | 27 +++ .../NewlineInNestedAnnotationFixerTest.php | 32 +++ .../src/Testing/AbstractCheckerTestCase.php | 32 +-- .../config/set/doctrine-annotations.php | 16 +- 8 files changed, 404 insertions(+), 19 deletions(-) create mode 100644 packages/coding-standard/src/Exception/EdgeFindingException.php create mode 100644 packages/coding-standard/src/Exception/NotImplementedYetException.php create mode 100644 packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php create mode 100644 packages/coding-standard/src/TokenRunner/Analyzer/FixerAnalyzer/DoctrineBlockFinder.php create mode 100644 packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/Fixture/basic_annotation_join.php.inc create mode 100644 packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/NewlineInNestedAnnotationFixerTest.php diff --git a/packages/coding-standard/src/Exception/EdgeFindingException.php b/packages/coding-standard/src/Exception/EdgeFindingException.php new file mode 100644 index 0000000000..ab70f5d71d --- /dev/null +++ b/packages/coding-standard/src/Exception/EdgeFindingException.php @@ -0,0 +1,11 @@ +doctrineBlockFinder = $doctrineBlockFinder; + + parent::__construct(); + } + + public function getDefinition(): FixerDefinitionInterface + { + return new FixerDefinition('Nested annotation should start on standalone line', []); + } + + /** + * @param iterable<\PhpCsFixer\Doctrine\Annotation\Token>&Tokens $tokens + */ + protected function fixAnnotations(Tokens $tokens): void + { + $tokenCount = count($tokens); + + $this->indentCounter = 0; + + // what about foreach? + for ($index = 0; $index < $tokenCount; ++$index) { + /** @var Token $currentToken */ + $currentToken = $tokens[$index]; + if (! $currentToken->isType(DocLexer::T_AT)) { + continue; + } + + /** @var Token|null $previousToken */ + $previousTokenPosition = $index - 1; + $previousToken = $tokens[$previousTokenPosition] ?? null; + if ($previousToken === null) { + continue; + } + + ++$this->indentCounter; + + // docblock opener → skip it + if ($previousToken->isType(DocLexer::T_NONE)) { + continue; + } + + if (! $previousToken->isType(DocLexer::T_OPEN_CURLY_BRACES)) { + throw new NotImplementedYetException(); + } + + // add a newline with indent - @todo resolve indent and removing of spaces + $indentWhitespace = str_repeat(' ', ($this->indentCounter - 1) * 4); + $tokens->insertAt($index, new Token(DocLexer::T_NONE, $indentWhitespace)); + $tokens->insertAt($index, new Token(DocLexer::T_NONE, ' * ')); + $tokens->insertAt($index, new Token(DocLexer::T_NONE, "\n")); + + $block = $this->doctrineBlockFinder->findInTokensByEdge($tokens, $previousTokenPosition); + if ($block !== null) { + $tokens->insertAt($block->getEnd(), new Token(DocLexer::T_NONE, ' * ')); + $tokens->insertAt($block->getEnd(), new Token(DocLexer::T_NONE, "\n")); + } + } + } +} diff --git a/packages/coding-standard/src/TokenRunner/Analyzer/FixerAnalyzer/DoctrineBlockFinder.php b/packages/coding-standard/src/TokenRunner/Analyzer/FixerAnalyzer/DoctrineBlockFinder.php new file mode 100644 index 0000000000..9774c83065 --- /dev/null +++ b/packages/coding-standard/src/TokenRunner/Analyzer/FixerAnalyzer/DoctrineBlockFinder.php @@ -0,0 +1,203 @@ + self::BLOCK_TYPE_CURLY_BRACE, + '}' => self::BLOCK_TYPE_CURLY_BRACE, + '(' => self::BLOCK_TYPE_PARENTHESIS_BRACE, + ')' => self::BLOCK_TYPE_PARENTHESIS_BRACE, + ]; + + /** + * @var string[] + */ + private const START_EDGES = ['(', '{']; + + /** + * @var mixed[] + */ + private $blockEndCache = []; + + /** + * Accepts position to both start and end token, e.g. (, ), {, } + */ + public function findInTokensByEdge(Tokens $tokens, int $position): ?BlockInfo + { + /** @var Token $token */ + $token = $tokens[$position]; + + /** @var Token $token */ + $blockType = $this->getBlockTypeByToken($token); + + if (in_array($token->getContent(), self::START_EDGES, true)) { + $blockStart = $position; + $blockEnd = $this->findOppositeBlockEdge($tokens, $blockType, $blockStart); + } else { + $blockEnd = $position; + $blockStart = $this->findOppositeBlockEdge($tokens, $blockType, $blockEnd, false); + } + + return new BlockInfo($blockStart, $blockEnd); + } + + private function getBlockTypeByToken(Token $token): int + { + return $this->getBlockTypeByContent($token->getContent()); + } + + private function getBlockTypeByContent(string $content): int + { + if (isset(self::CONTENT_TO_BLOCK_TYPE[$content])) { + return self::CONTENT_TO_BLOCK_TYPE[$content]; + } + + throw new MissingImplementationException(sprintf( + 'Implementation is missing for "%s" in "%s". Just add it to "%s" property with proper block type', + $content, + __METHOD__, + '$contentToBlockType' + )); + } + + /** + * @param int $type type of block, one of BLOCK_TYPE_* + * @param int $searchIndex index of starting brace + * @param bool $findEnd if method should find block's end or start + * + * @return int index of opposite brace + * + * @copied from + * @see \PhpCsFixer\Tokenizer\Tokens::findBlockEnd() + */ + private function findOppositeBlockEdge(Tokens $tokens, $type, $searchIndex, bool $findEnd = true) + { + $blockEdgeDefinitions = $this->getBlockEdgeDefinitions(); + if (! isset($blockEdgeDefinitions[$type])) { + throw new EdgeFindingException(sprintf('Invalid param type: "%s".', $type)); + } + + if (isset($this->blockEndCache[$searchIndex])) { + return $this->blockEndCache[$searchIndex]; + } + + $startEdge = $blockEdgeDefinitions[$type]['start']; + $endEdge = $blockEdgeDefinitions[$type]['end']; + $startIndex = $searchIndex; + + $endIndex = $tokens->count() - 1; + $indexOffset = 1; + + if (! $findEnd) { + [$startEdge, $endEdge] = [$endEdge, $startEdge]; + $indexOffset = -1; + $endIndex = 0; + } + + $this->ensureStartTokenIsNotStartEdge($tokens, $startIndex, $startEdge, $findEnd); + + $index = $this->resolveIndexForBlockLevel($startIndex, $endIndex, $tokens, $startEdge, $endEdge, $indexOffset); + + /** @var Token $currentToken */ + $currentToken = $tokens[$index]; + if ($currentToken->getContent() !== $endEdge) { + $message = sprintf('Missing block "%s".', $findEnd ? 'end' : 'start'); + throw new EdgeFindingException($message); + } + + $this->blockEndCache[$startIndex] = $index; + $this->blockEndCache[$index] = $startIndex; + + return $index; + } + + /** + * @return array> + */ + private function getBlockEdgeDefinitions(): array + { + return [ + self::BLOCK_TYPE_CURLY_BRACE => [ + 'start' => '{', + 'end' => '}', + ], + self::BLOCK_TYPE_PARENTHESIS_BRACE => [ + 'start' => '(', + 'end' => ')', + ], + ]; + } + + private function resolveIndexForBlockLevel( + int $startIndex, + int $endIndex, + Tokens $tokens, + string $startEdge, + string $endEdge, + int $indexOffset + ): int { + $blockLevel = 0; + + for ($index = $startIndex; $index !== $endIndex; $index += $indexOffset) { + /** @var Token $token */ + $token = $tokens[$index]; + + if ($token->getContent() === $startEdge) { + ++$blockLevel; + + continue; + } + + if ($token->getContent() === $endEdge) { + --$blockLevel; + + if ($blockLevel === 0) { + break; + } + + continue; + } + } + return $index; + } + + private function ensureStartTokenIsNotStartEdge( + Tokens $tokens, + int $startIndex, + string $startEdge, + bool $findEnd + ): void { + /** @var Token $startToken */ + $startToken = $tokens[$startIndex]; + + if ($startToken->getContent() !== $startEdge) { + throw new EdgeFindingException(sprintf( + 'Invalid param $startIndex - not a proper block "%s".', + $findEnd ? 'start' : 'end' + )); + } + } +} diff --git a/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/Fixture/basic_annotation_join.php.inc b/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/Fixture/basic_annotation_join.php.inc new file mode 100644 index 0000000000..5265e3a31c --- /dev/null +++ b/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/Fixture/basic_annotation_join.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/NewlineInNestedAnnotationFixerTest.php b/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/NewlineInNestedAnnotationFixerTest.php new file mode 100644 index 0000000000..0389685924 --- /dev/null +++ b/packages/coding-standard/tests/Fixer/Annotation/NewlineInNestedAnnotationFixer/NewlineInNestedAnnotationFixerTest.php @@ -0,0 +1,32 @@ +doTestFileInfo($fileInfo); + } + + public function provideData(): Iterator + { + return StaticFixtureFinder::yieldDirectory(__DIR__ . '/Fixture'); + } + + protected function getCheckerClass(): string + { + return NewlineInNestedAnnotationFixer::class; + } +} diff --git a/packages/easy-coding-standard-tester/src/Testing/AbstractCheckerTestCase.php b/packages/easy-coding-standard-tester/src/Testing/AbstractCheckerTestCase.php index 7006c4f250..10dc6052a3 100644 --- a/packages/easy-coding-standard-tester/src/Testing/AbstractCheckerTestCase.php +++ b/packages/easy-coding-standard-tester/src/Testing/AbstractCheckerTestCase.php @@ -37,11 +37,6 @@ abstract class AbstractCheckerTestCase extends AbstractKernelTestCase */ private $errorAndDiffCollector; - /** - * @var SmartFileInfo|null - */ - private $activeFileInfo; - protected function setUp(): void { $configs = $this->getValidatedConfigs(); @@ -68,7 +63,7 @@ protected function doTestFileInfo(SmartFileInfo $fileInfo): void $fixtureSplitter = new StaticFixtureSplitter(); [$beforeFileInfo, $afterFileInfo] = $fixtureSplitter->splitFileInfoToLocalInputAndExpectedFileInfos($fileInfo); - $this->doTestWrongToFixedFile($beforeFileInfo, $afterFileInfo->getRealPath()); + $this->doTestWrongToFixedFile($beforeFileInfo, $afterFileInfo->getRealPath(), $fileInfo); } protected function getCheckerClass(): string @@ -132,7 +127,7 @@ protected function doTestCorrectFileInfo(SmartFileInfo $fileInfo): void if ($this->fixerFileProcessor->getCheckers() !== []) { $processedFileContent = $this->fixerFileProcessor->processFile($fileInfo); - $this->assertStringEqualsWithFileLocation($fileInfo->getRealPath(), $processedFileContent); + $this->assertStringEqualsWithFileLocation($fileInfo->getRealPath(), $processedFileContent, $fileInfo); } if ($this->sniffFileProcessor->getCheckers() !== []) { @@ -145,7 +140,7 @@ protected function doTestCorrectFileInfo(SmartFileInfo $fileInfo): void ); $this->assertSame(0, $this->errorAndDiffCollector->getErrorCount(), $failedAssertMessage); - $this->assertStringEqualsWithFileLocation($fileInfo->getRealPath(), $processedFileContent); + $this->assertStringEqualsWithFileLocation($fileInfo->getRealPath(), $processedFileContent, $fileInfo); } } @@ -165,21 +160,24 @@ protected function doTestFileInfoWithErrorCountOf(SmartFileInfo $wrongFileInfo, $this->assertSame($errorCount, $this->errorAndDiffCollector->getErrorCount(), $message); } - private function doTestWrongToFixedFile(SmartFileInfo $wrongFileInfo, string $fixedFile): void - { + private function doTestWrongToFixedFile( + SmartFileInfo $wrongFileInfo, + string $fixedFile, + SmartFileInfo $fixtureFileInfo + ): void { $this->ensureSomeCheckersAreRegistered(); if ($this->fixerFileProcessor->getCheckers() !== []) { $processedFileContent = $this->fixerFileProcessor->processFile($wrongFileInfo); - $this->assertStringEqualsWithFileLocation($fixedFile, $processedFileContent); + $this->assertStringEqualsWithFileLocation($fixedFile, $processedFileContent, $fixtureFileInfo); } if ($this->sniffFileProcessor->getCheckers() !== []) { $processedFileContent = $this->sniffFileProcessor->processFile($wrongFileInfo); } - $this->assertStringEqualsWithFileLocation($fixedFile, $processedFileContent); + $this->assertStringEqualsWithFileLocation($fixedFile, $processedFileContent, $fixtureFileInfo); } private function autoloadCodeSniffer(): void @@ -223,10 +221,12 @@ private function ensureSomeCheckersAreRegistered(): void ); } - private function assertStringEqualsWithFileLocation(string $file, string $processedFileContent): void - { - $message = 'Caused by ' . ($this->activeFileInfo !== null ? $this->activeFileInfo->getRealPath() : $file); - + private function assertStringEqualsWithFileLocation( + string $file, + string $processedFileContent, + SmartFileInfo $fixtureFileInfo + ): void { + $message = $fixtureFileInfo->getRelativeFilePathFromCwd(); $this->assertStringEqualsFile($file, $processedFileContent, $message); } diff --git a/packages/easy-coding-standard/config/set/doctrine-annotations.php b/packages/easy-coding-standard/config/set/doctrine-annotations.php index f224a8085f..0eb1239663 100644 --- a/packages/easy-coding-standard/config/set/doctrine-annotations.php +++ b/packages/easy-coding-standard/config/set/doctrine-annotations.php @@ -7,13 +7,23 @@ use PhpCsFixer\Fixer\DoctrineAnnotation\DoctrineAnnotationIndentationFixer; use PhpCsFixer\Fixer\DoctrineAnnotation\DoctrineAnnotationSpacesFixer; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symplify\CodingStandard\Fixer\Annotation\NewlineInNestedAnnotationFixer; return static function (ContainerConfigurator $containerConfigurator): void { $services = $containerConfigurator->services(); - // doctrine annotations - $services->set(DoctrineAnnotationIndentationFixer::class); + $services->set(DoctrineAnnotationIndentationFixer::class) + ->call('configure', [[ + 'indent_mixed_lines' => true, + ]]); + $services->set(DoctrineAnnotationBracesFixer::class); - $services->set(DoctrineAnnotationSpacesFixer::class); + $services->set(DoctrineAnnotationSpacesFixer::class) + ->call('configure', [[ + 'after_array_assignments_equals' => true, + 'before_array_assignments_equals' => true, + ]]); + $services->set(DoctrineAnnotationArrayAssignmentFixer::class); + $services->set(NewlineInNestedAnnotationFixer::class); }; From 450e420bf0d41f8e63587a4c6ee8c395843a2e3d Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 18 Aug 2020 12:14:07 +0200 Subject: [PATCH 2/4] [CS] update README and prepare for split --- .../docs/php_code_sniffer_sniffs.md | 0 .../docs/phpcs_fixer_fixers.md | 36 +++++++++++++++++++ .../coding-standard/docs/phpstan_rules.md | 0 .../NewlineInNestedAnnotationFixer.php | 2 +- 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 packages/coding-standard/docs/php_code_sniffer_sniffs.md create mode 100644 packages/coding-standard/docs/phpcs_fixer_fixers.md create mode 100644 packages/coding-standard/docs/phpstan_rules.md diff --git a/packages/coding-standard/docs/php_code_sniffer_sniffs.md b/packages/coding-standard/docs/php_code_sniffer_sniffs.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/coding-standard/docs/phpcs_fixer_fixers.md b/packages/coding-standard/docs/phpcs_fixer_fixers.md new file mode 100644 index 0000000000..7e99ddf135 --- /dev/null +++ b/packages/coding-standard/docs/phpcs_fixer_fixers.md @@ -0,0 +1,36 @@ +# PHP CS Fixer - Fixers + +### Indent nested Annotations to Newline + +- class: [`Symplify\CodingStandard\Fixer\Annotation\NewlineInNestedAnnotationFixer`](packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php) + +```php +services(); + $services->set(Symplify\CodingStandard\Fixer\Annotation\NewlineInNestedAnnotationFixer::class); +}; +``` + +```diff + use Doctrine\ORM\Mapping as ORM; + + /** +- * @ORM\Table(name="user", indexes={@ORM\Index(name="user_id", columns={"another_id"})}) ++ * @ORM\Table(name="user", indexes={ ++ * @ORM\Index(name="user_id", columns={"another_id"}) ++ * }) + */ + class SomeEntity + { + } +``` + +
diff --git a/packages/coding-standard/docs/phpstan_rules.md b/packages/coding-standard/docs/phpstan_rules.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php b/packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php index 8349a6ade3..d3c4b09cbb 100644 --- a/packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php +++ b/packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php @@ -45,7 +45,7 @@ public function getDefinition(): FixerDefinitionInterface */ protected function fixAnnotations(Tokens $tokens): void { - $tokenCount = count($tokens); + $tokenCount = $tokens->count(); $this->indentCounter = 0; From bdee596d4285fcc3f37185c062b65b70b1490d4d Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Tue, 18 Aug 2020 13:15:24 +0200 Subject: [PATCH 3/4] [CS] Split long god-README to smaller 3 files files - fixes, sniff and rules --- ci/check_coding_standard_readme_sync.php | 14 +- packages/coding-standard/README.md | 1252 +---------------- .../docs/php_code_sniffer_sniffs.md | 30 + .../docs/phpcs_fixer_fixers.md | 232 ++- .../coding-standard/docs/phpstan_rules.md | 928 ++++++++++++ .../src/DependencyUpdater.php | 1 + .../set-config-resolver/src/SetResolver.php | 8 +- phpstan.neon | 2 + 8 files changed, 1207 insertions(+), 1260 deletions(-) diff --git a/ci/check_coding_standard_readme_sync.php b/ci/check_coding_standard_readme_sync.php index 1eedf5214e..e67e8be44f 100755 --- a/ci/check_coding_standard_readme_sync.php +++ b/ci/check_coding_standard_readme_sync.php @@ -22,7 +22,7 @@ final class CodingStandardSyncChecker /** * @var string */ - private const CODING_STANDARD_README_PATH = __DIR__ . '/../packages/coding-standard/README.md'; + private const CODING_STANDARD_DOCS_GLOB_PATH = __DIR__ . '/../packages/coding-standard/*/**.md'; /** * @see https://regex101.com/r/Unygf7/3/ @@ -69,12 +69,16 @@ public function run(): void */ private function resolveCheckerClassesInReadme(): array { - $codingStandardReadmeContent = $this->smartFileSystem->readFile(self::CODING_STANDARD_README_PATH); - $checkerClassMatches = Strings::matchAll($codingStandardReadmeContent, self::CHECKER_CLASS_PATTERN); + $filePaths = glob(self::CODING_STANDARD_DOCS_GLOB_PATH); $checkerClasses = []; - foreach ($checkerClassMatches as $checkerClassMatch) { - $checkerClasses[] = $checkerClassMatch['checker_class']; + foreach ($filePaths as $filePath) { + $docFileContent = $this->smartFileSystem->readFile($filePath); + + $checkerClassMatches = Strings::matchAll($docFileContent, self::CHECKER_CLASS_PATTERN); + foreach ($checkerClassMatches as $checkerClassMatch) { + $checkerClasses[] = $checkerClassMatch['checker_class']; + } } $checkerClasses = array_unique($checkerClasses); diff --git a/packages/coding-standard/README.md b/packages/coding-standard/README.md index 8dd973a3d2..663db6542d 100644 --- a/packages/coding-standard/README.md +++ b/packages/coding-standard/README.md @@ -29,1253 +29,11 @@ includes: ## Rules Overview -- Jump to [Object Calisthenics rules](#object-calisthenics-rules) - -
- -### Cognitive Complexity for Method and Class Must be Less than X - -- [Why it's the best rule in your coding standard?](https://www.tomasvotruba.com/blog/2018/05/21/is-your-code-readable-by-humans-cognitive-complexity-tells-you/) - -**For PHPStan** - -- class: [`FunctionLikeCognitiveComplexityRule`](packages/coding-standard/packages/cognitive-complexity/src/Rules/FunctionLikeCognitiveComplexityRule.php) -- class: [`ClassLikeCognitiveComplexityRule`](packages/coding-standard/packages/cognitive-complexity/src/Rules/ClassLikeCognitiveComplexityRule.php) - -```yaml -# phpstan.neon -includes: - - vendor/symplify/coding-standard/packages/cognitive-complexity/config/cognitive-complexity-rules.neon - -parameters: - symplify: - max_cognitive_complexity: 8 # default - max_class_cognitive_complexity: 50 # default -``` - -:x: - -```php - - -### Classes with Static Methods must have "Static" in the Name - -- class: [`Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule`](src/Rules/NoClassWithStaticMethodWithoutStaticNameRule.php) - -Be honest about static. [Why is static bad?](https://tomasvotruba.com/blog/2019/04/01/removing-static-there-and-back-again/) - -Value object static constructors, EventSubscriber and Command classe are excluded. - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule -``` - -:x: - -```php - - -### Remove Extra Spaces around Property and Constants Modifiers - -- class: [`Symplify\CodingStandard\Fixer\Spacing\RemoveSpacingAroundModifierAndConstFixer`](packages/coding-standard/src/Fixer/Spacing/RemoveSpacingAroundModifierAndConstFixer.php) - -```php -services(); - $services->set(Symplify\CodingStandard\Fixer\Spacing\RemoveSpacingAroundModifierAndConstFixer::class); -}; -``` - -```diff - class SomeClass - { -- protected static $value; -+ protected static $value; -} -``` - -
- -### Use Unique Class Short Names - -- class: [`Symplify\CodingStandard\Rules\NoDuplicatedShortClassNameRule`](src/Rules/NoDuplicatedShortClassNameRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDuplicatedShortClassNameRule -``` - -:x: - -```php - - -### Make `@param`, `@return` and `@var` Format United - -- class: [`ParamReturnAndVarTagMalformsFixer`](src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php) - -```php -services(); - $services->set(Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer::class); -}; -``` - -```diff - - -### Prefer Another Class - -- class: [`PreferredClassRule`](src/Rules/PreferredClassRule.php) - -```yaml -# phpstan.neon -parameters: - symplify: - old_to_preffered_classes: - DateTime: 'Nette\Utils\DateTime' - -rules: - - Symplify\CodingStandard\Rules\PreferredClassRule -``` - -:x: - -```php - - -### Require @see annotation to class Test case by Type - -- class: [`SeeAnnotationToTestRule`](src/Rules/SeeAnnotationToTestRule.php) - -```yaml -# phpstan.neon -parameters: - symplify: - required_see_types: - - PHPStan\Rules\Rule - -rules: - - Symplify\CodingStandard\Rules\SeeAnnotationToTestRule -``` - -:x: - -```php - - -### Defined Method Argument should be Always Constant Value - -- class: [`ForceMethodCallArgumentConstantRule`](src/Rules/ForceMethodCallArgumentConstantRule.php) - -```yaml -# phpstan.neon -parameters: - symplify: - constant_arg_by_method_by_type: - AlwaysCallMeWithConstant: - some_type: [0] # positions - -rules: - - Symplify\CodingStandard\Rules\ForceMethodCallArgumentConstantRule -``` - -:x: - -```php -call('someValue'); - // should be: $alwaysCallMeWithConstant->call(TypeList::SOME); - - } -} -``` - -
- -### Indexed PHP arrays should have 1 item per line - -- class: [`StandaloneLineInMultilineArrayFixer`](src/Fixer/ArrayNotation/StandaloneLineInMultilineArrayFixer.php) - -```php -services(); - $services->set(Symplify\CodingStandard\Fixer\ArrayNotation\StandaloneLineInMultilineArrayFixer::class); -}; -``` - -```diff --$friends = [1 => 'Peter', 2 => 'Paul']; -+$friends = [ -+ 1 => 'Peter', -+ 2 => 'Paul' -+]; -``` - -
- -### Block comment should not have 2 empty lines in a row - -- class: [`RemoveSuperfluousDocBlockWhitespaceFixer`](src/Fixer/Commenting/RemoveSuperfluousDocBlockWhitespaceFixer.php) - -```php -services(); - $services->set(RemoveSuperfluousDocBlockWhitespaceFixer::class); -}; -``` - -```diff - /** - * @param int $value - * -- * - * @return array - */ - public function setCount($value) - { - } -``` - -
- -### Parameters, Arguments and Array items should be on the same/standalone line to fit Line Length - -- class: [`LineLengthFixer`](src/Fixer/LineLength/LineLengthFixer.php) - -```php -services(); - $services->set(LineLengthFixer::class) - ->call('configure', [[ - LineLengthFixer::LINE_LENGTH => 120, - LineLengthFixer::BREAK_LONG_LINES => true, - LineLengthFixer::INLINE_SHORT_LINES => true, - ]]); -}; -```` - -```diff - class SomeClass - { -- public function someMethod(SuperLongArguments $superLongArguments, AnotherLongArguments $anotherLongArguments, $oneMore) -+ public function someMethod( -+ SuperLongArguments $superLongArguments, -+ AnotherLongArguments $anotherLongArguments, -+ $oneMore -+ ) - { - } - -- public function someOtherMethod( -- ShortArgument $shortArgument, -- $oneMore -- ) { -+ public function someOtherMethod(ShortArgument $shortArgument, $oneMore) { - } - } -``` - -
- -### Strict types declaration has to be followed by empty line - -- class: [`BlankLineAfterStrictTypesFixer`](src/Fixer/Strict/BlankLineAfterStrictTypesFixer.php) - -```php -services(); - $services->set(Symplify\CodingStandard\Fixer\Strict\BlankLineAfterStrictTypesFixer::class); -}; -``` - -```diff - - -### Constant type Must Match its Value - -- class: [`MatchingTypeConstantRule`](src/Rules/MatchingTypeConstantRule.php) - -```php -services(); - $services->set(Symplify\CodingStandard\Rules\MatchingTypeConstantRule::class); -}; -``` - -:x: - -```php - - -### Boolish Methods has to have is/has/was Name - -- class: [`BoolishClassMethodPrefixRule`](src/Rules/BoolishClassMethodPrefixRule.php) - -```php -services(); - $services->set(BoolishClassMethodPrefixRule::class); -}; -``` - -:x: - -```php -age > 100; - } -} -``` - -:+1: - -```php -age > 100; - } -} -``` - -
- -### Forbidden return of `require_once()`/`incude_once()` - -- class: [`ForbidReturnValueOfIncludeOnceRule`](src/Rules/ForbidReturnValueOfIncludeOnceRule.php) - -```php -services(); - $services->set(ForbidReturnValueOfIncludeOnceRule::class); -}; -``` - -:x: - -```php - - -### Use custom exceptions instead of Native Ones - -- class: [`NoDefaultExceptionRule`](src/Rules/NoDefaultExceptionRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDefaultExceptionRule -``` - -:x: - -```php - - -### Class "%s" inherits from forbidden parent class "%s". Use composition over inheritance instead - -- class: [`ForbiddenParentClassRule`](src/Rules/ForbiddenParentClassRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\ForbiddenParentClassRule - -parameters: - symplify: - forbidden_parent_classes: - - 'Doctrine\ORM\EntityRepository' - # you can use fnmatch() pattern - - '*\AbstractController' -``` - -:x: - -```php -entityRepository = $entityRepository; - } -} -``` - -
- -### Use explicit return values over magic "&$variable" reference - -- class: [`NoReferenceRule`](src/Rules/NoReferenceRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoReferenceRule -``` - -:x: - -```php - - -### Use explicit Method Names over Dynamic - -- class: [`NoDynamicMethodNameRule`](src/Rules/NoDynamicMethodNameRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDynamicMethodNameRule -``` - -:x: - -```php -$value(); - } -} -``` - -
- -### No isset on objects - -- class: [`NoIssetOnObjectRule`](src/Rules/NoIssetOnObjectRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoIssetOnObjectRule -``` - -:x: - -```php - - -### No Function Call on Method Call - -- class: [`NoFunctionCallInMethodCallRule`](src/Rules/NoFunctionCallInMethodCallRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoFunctionCallInMethodCallRule -``` - -:x: - -```php -someMethod(strlen('fooo')); - } - - private function someMethod($value) - { - return $value; - } -} -``` - -:+1: - -```php -someMethod($fooSize); - } - - private function someMethod($value) - { - return $value; - } -} -``` - -
- -### No Array Access on Object - -- class: [`NoArrayAccessOnObjectRule`](src/Rules/NoArrayAccessOnObjectRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoArrayAccessOnObjectRule -``` - -:x: - -```php - - -### Use explicit Property Fetch Names over Dynamic - -- class: [`NoDynamicPropertyFetchNameRule`](src/Rules/NoDynamicPropertyFetchNameRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDynamicPropertyFetchNameRule -``` - -:x: - -```php -$value; - } -} -``` - -
- -### Prevent Override of Parent Method Visbility - -- class: [`PreventParentMethodVisibilityOverrideRule`](src/Rules/PreventParentMethodVisibilityOverrideRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\PreventParentMethodVisibilityOverrideRule -``` - -```php - - -### Use explicit comparison over empty() - -- class: [`NoEmptyRule`](src/Rules/NoEmptyRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoEmptyRule -``` - -:x: - -```php - - -### There should not be comments with valid code - -- class: [`CommentedOutCodeSniff`](src/Sniffs/Debug/CommentedOutCodeSniff.php) - -```php -services(); - $services->set(CommentedOutCodeSniff::class); -}; -``` - -:x: - -```php - - -### Debug functions Cannot Be left in the Code - -- class: [`NoDebugFuncCallRule`](src/Rules/NoDebugFuncCallRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDebugFuncCallRule -``` - -:x: - -```php - - -### Class should have suffix by parent class/interface - -Covers `Interface` suffix as well, e.g `EventSubscriber` checks for `EventSubscriberInterface` as well. - -- class: [`ClassNameRespectsParentSuffixRule`](src/Rules/ClassNameRespectsParentSuffixRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\ClassNameRespectsParentSuffixRule - -parameters: - symplify: - parent_classes: - - Rector - - Rule -``` - -:x: - -```php - - -### No Parameter can Have Default Value - -- class: [`NoDefaultParameterValueRule`](src/Rules/NoDefaultParameterValueRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoDefaultParameterValueRule -``` - -:x: - -```php - - -### No Parameter can be Nullable - -Inspired by [Null Hell](https://afilina.com/null-hell) by @afilina - -- class: [`NoNullableParameterRule`](src/Rules/NoNullableParameterRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\Rules\NoNullableParameterRule -``` - -:x: - -```php - - -## Object Calisthenics rules - -- From [Object Calisthenics](https://tomasvotruba.com/blog/2017/06/26/php-object-calisthenics-rules-made-simple-version-3-0-is-out-now/) -- [Original source for PHPStan rules](https://github.com/object-calisthenics/phpcs-calisthenics-rules/) - -### No `else` And `elseif` - -- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoElseAndElseIfRule`](packages/object-calisthenics/src/Rules/NoElseAndElseIfRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoElseAndElseIfRule -``` - -:x: - -```php - - -### No Names Shorter than 3 Chars - -- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoShortNameRule`](packages/object-calisthenics/src/Rules/NoShortNameRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoShortNameRule -``` - -:x: - -```php - - -### No setter methods - -- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoSetterClassMethodRule`](packages/coding-standard/src/Rules/ObjectCalisthenics/NoSetterClassMethodRule.php) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoSetterClassMethodRule -``` - -:x: - -```php -name = $name; - } -} -``` - -
- -### No Chain Method Call - -- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoChainMethodCallRule`](packages/coding-standard/src/Rules/ObjectCalisthenics/NoChainMethodCallRule.php) -- Check [Fluent Interfaces are Evil](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) - -```yaml -# phpstan.neon -rules: - - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoChainMethodCallRule -``` - -:x: - -```php -create()->modify()->save(); - } -} -``` - -:+1: - -```php -create(); - $object->modify(); - $object->save(); - - return $object; - } -} -``` +- [PHP_CodeSniffer Sniffs](/docs/php_code_sniffer_sniffs.md) +- [PHP CS Fixer Fixers](/docs/phpcs_fixer_fixers.md) +- [PHPStan rules](/docs/phpstan_rules.md) +- [Object Calisthenics rules](/docs/phpstan_rules.md#object-calisthenics-rules) +- [Cognitive Complexity rules](/docs/phpstan_rules.md#cognitive-complexity)
diff --git a/packages/coding-standard/docs/php_code_sniffer_sniffs.md b/packages/coding-standard/docs/php_code_sniffer_sniffs.md index e69de29bb2..aa69fde358 100644 --- a/packages/coding-standard/docs/php_code_sniffer_sniffs.md +++ b/packages/coding-standard/docs/php_code_sniffer_sniffs.md @@ -0,0 +1,30 @@ +# PHP Code Sniffer Sniffs + +## There should not be comments with valid code + +- class: [`Symplify\CodingStandard\Sniffs\Debug\CommentedOutCodeSniff`](src/Sniffs/Debug/CommentedOutCodeSniff.php) + +```php +services(); + $services->set(CommentedOutCodeSniff::class); +}; +``` + +```php +services(); + $services->set(Symplify\CodingStandard\Fixer\Strict\BlankLineAfterStrictTypesFixer::class); +}; +``` + +```diff + + +## Parameters, Arguments and Array items should be on the same/standalone line to fit Line Length + +- class: [`LineLengthFixer`](src/Fixer/LineLength/LineLengthFixer.php) + +```php +services(); + $services->set(LineLengthFixer::class) + ->call('configure', [[ + LineLengthFixer::LINE_LENGTH => 120, + LineLengthFixer::BREAK_LONG_LINES => true, + LineLengthFixer::INLINE_SHORT_LINES => true, + ]]); +}; +```` + +```diff + class SomeClass + { +- public function someMethod(SuperLongArguments $superLongArguments, AnotherLongArguments $anotherLongArguments, $oneMore) ++ public function someMethod( ++ SuperLongArguments $superLongArguments, ++ AnotherLongArguments $anotherLongArguments, ++ $oneMore ++ ) + { + } + +- public function someOtherMethod( +- ShortArgument $shortArgument, +- $oneMore +- ) { ++ public function someOtherMethod(ShortArgument $shortArgument, $oneMore) { + } + } +``` + +
+ +## Block comment should not have 2 empty lines in a row + +- class: [`RemoveSuperfluousDocBlockWhitespaceFixer`](src/Fixer/Commenting/RemoveSuperfluousDocBlockWhitespaceFixer.php) + +```php +services(); + $services->set(RemoveSuperfluousDocBlockWhitespaceFixer::class); +}; +``` + +```diff + /** + * @param int $value + * +- * + * @return array + */ + public function setCount($value) + { + } +``` + +
+ +## Indexed PHP arrays should have 1 item per line + +- class: [`StandaloneLineInMultilineArrayFixer`](src/Fixer/ArrayNotation/StandaloneLineInMultilineArrayFixer.php) + +```php +services(); + $services->set(Symplify\CodingStandard\Fixer\ArrayNotation\StandaloneLineInMultilineArrayFixer::class); +}; +``` + +```diff +-$friends = [1 => 'Peter', 2 => 'Paul']; ++$friends = [ ++ 1 => 'Peter', ++ 2 => 'Paul' ++]; +``` + +
+ +## Make `@param`, `@return` and `@var` Format United + +- class: [`ParamReturnAndVarTagMalformsFixer`](src/Fixer/Commenting/ParamReturnAndVarTagMalformsFixer.php) + +```php +services(); + $services->set(Symplify\CodingStandard\Fixer\Commenting\ParamReturnAndVarTagMalformsFixer::class); +}; +``` + +```diff + + +## Remove Extra Spaces around Property and Constants Modifiers + +- class: [`Symplify\CodingStandard\Fixer\Spacing\RemoveSpacingAroundModifierAndConstFixer`](packages/coding-standard/src/Fixer/Spacing/RemoveSpacingAroundModifierAndConstFixer.php) + +```php +services(); + $services->set(Symplify\CodingStandard\Fixer\Spacing\RemoveSpacingAroundModifierAndConstFixer::class); +}; +``` + +```diff + class SomeClass + { +- protected static $value; ++ protected static $value; +} +``` + +
+ +## Indent nested Annotations to Newline - class: [`Symplify\CodingStandard\Fixer\Annotation\NewlineInNestedAnnotationFixer`](packages/coding-standard/src/Fixer/Annotation/NewlineInNestedAnnotationFixer.php) diff --git a/packages/coding-standard/docs/phpstan_rules.md b/packages/coding-standard/docs/phpstan_rules.md index e69de29bb2..af812ea5c2 100644 --- a/packages/coding-standard/docs/phpstan_rules.md +++ b/packages/coding-standard/docs/phpstan_rules.md @@ -0,0 +1,928 @@ +# PHPStan Rules + +## No Parameter can be Nullable + +Inspired by [Null Hell](https://afilina.com/null-hell) by @afilina + +- class: [`NoNullableParameterRule`](src/Rules/NoNullableParameterRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoNullableParameterRule +``` + +```php +class SomeClass +{ + public function run(?string $vaulue = true) + { + } +} +``` + +:x: + +## No Parameter can Have Default Value + +- class: [`NoDefaultParameterValueRule`](src/Rules/NoDefaultParameterValueRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDefaultParameterValueRule +``` + +```php +class SomeClass +{ + public function run($vaulue = true) + { + } +} +``` + +:x: + +## Class should have suffix by parent class/interface + +Covers `Interface` suffix as well, e.g `EventSubscriber` checks for `EventSubscriberInterface` as well. + +- class: [`ClassNameRespectsParentSuffixRule`](src/Rules/ClassNameRespectsParentSuffixRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\ClassNameRespectsParentSuffixRule + +parameters: + symplify: + parent_classes: + - Rector + - Rule +``` + +:x: + +```php +class Some extends Command // should be "SomeCommand" +{ +} +``` + +
+ +## Debug functions Cannot Be left in the Code + +- class: [`NoDebugFuncCallRule`](src/Rules/NoDebugFuncCallRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDebugFuncCallRule +``` + +:x: + +```php +d($value); +dd($value); +dump($value); +var_dump($value); +``` + +
+ +## Use explicit comparison over `empty()` + +- class: [`NoEmptyRule`](src/Rules/NoEmptyRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoEmptyRule +``` + +:x: + +```php +final class SomeClass +{ + public function run($value) + { + return empty($value); + } +} +``` + +:+1: + +```php +final class SomeClass +{ + public function run(array $value) + { + return $value === []; + } +} +``` + + +## Prevent Override of Parent Method Visbility + +- class: [`PreventParentMethodVisibilityOverrideRule`](src/Rules/PreventParentMethodVisibilityOverrideRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\PreventParentMethodVisibilityOverrideRule +``` + +```php +class ProtectedVisibility +{ + protected function run() + { + } +} + +final class PublicOverride extends ProtectedVisibility +{ + public function run() + { + } +} +``` + +:x: + +
+ +## Use explicit Property Fetch Names over Dynamic + +- class: [`NoDynamicPropertyFetchNameRule`](src/Rules/NoDynamicPropertyFetchNameRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDynamicPropertyFetchNameRule +``` + +```php +final class DynamicPropertyFetchName +{ + public function run($value) + { + $this->$value; + } +} +``` + +:x: + +
+ +## No Function Call on Method Call + +- class: [`NoFunctionCallInMethodCallRule`](src/Rules/NoFunctionCallInMethodCallRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoFunctionCallInMethodCallRule +``` + +```php +final class SomeClass +{ + public function run($value): void + { + $this->someMethod(strlen('fooo')); + } + + private function someMethod($value) + { + return $value; + } +} +``` + +:x: + +```php +final class SomeClass +{ + public function run($value): void + { + $fooSize = strlen('fooo'); + $this->someMethod($fooSize); + } + + private function someMethod($value) + { + return $value; + } +} +``` + +:+1: + +
+ +## No Array Access on Object + +- class: [`NoArrayAccessOnObjectRule`](src/Rules/NoArrayAccessOnObjectRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoArrayAccessOnObjectRule +``` + +```php +final class MagicArrayObject implements ArrayAccess +{ + public function offsetExists($offset) + { + // ... + } + + public function offsetGet($offset) + { + // ... + } + + public function offsetSet($offset,$value) + { + // ... + } + + public function offsetUnset($offset) + { + // ... + } +} +``` + + +```php +final class SomeClass +{ + public function run(MagicArrayObject $magicArrayObject) + { + return $magicArrayObject['more_magic']; + } +} +``` + +:x: + +
+ + + +:+1: + + +## No isset on objects + +- class: [`NoIssetOnObjectRule`](src/Rules/NoIssetOnObjectRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoIssetOnObjectRule +``` + +```php +final class IssetOnObject +{ + public function run() + { + if (mt_rand(0, 1)) { + $object = new SomeClass(); + } + + if (isset($object)) { + return $object; + } + } +} +``` + +:x: + +```php +final class IssetOnObject +{ + public function run() + { + $object = null; + if (mt_rand(0, 1)) { + $object = new SomeClass(); + } + + if ($object !== null) { + return $object; + } + } +} +``` + +:+1: + +
+ +## Use explicit Method Names over Dynamic + +- class: [`NoDynamicMethodNameRule`](src/Rules/NoDynamicMethodNameRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDynamicMethodNameRule +``` + +```php +final class DynamicMethodCallName +{ + public function run($value) + { + $this->$value(); + } +} +``` + +:x: + + +## Use explicit return values over magic "&$variable" reference + +- class: [`NoReferenceRule`](src/Rules/NoReferenceRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoReferenceRule +``` + +```php +function someFunction(&$var) +{ + $var + 1; +} +``` + +:x: + +```php +function someFunction($var) +{ + return $var + 1; +} +``` + +:+1: + + +
+ +## Class "%s" inherits from forbidden parent class "%s". Use Composition over Inheritance instead + +- class: [`ForbiddenParentClassRule`](src/Rules/ForbiddenParentClassRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\ForbiddenParentClassRule + +parameters: + symplify: + forbidden_parent_classes: + - 'Doctrine\ORM\EntityRepository' + # you can use fnmatch() pattern + - '*\AbstractController' +``` + +```php +use Doctrine\ORM\EntityRepository; + +final class ProductRepository extends EntityRepository +{ +} +``` + +:x: + +```php +use Doctrine\ORM\EntityRepository; + +final class ProductRepository +{ + /** + * @var EntityRepository + */ + private $entityRepository; + + public function __construct(EntityRepository $entityRepository) + { + $this->entityRepository = $entityRepository; + } +} +``` + +:+1: + +
+ +## Use custom exceptions instead of Native Ones + +- class: [`NoDefaultExceptionRule`](src/Rules/NoDefaultExceptionRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDefaultExceptionRule +``` + +```php +throw new RuntimeException('...'); +``` + +:x: + +```php +use App\Exception\FileNotFoundExceptoin; + +throw new FileNotFoundExceptoin('...'); +``` + +:+1: + +
+ +## Forbidden return of `require_once()`/`incude_once()` + +- class: [`ForbidReturnValueOfIncludeOnceRule`](src/Rules/ForbidReturnValueOfIncludeOnceRule.php) + +```php +// ecs.php + +declare(strict_types=1); + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symplify\CodingStandard\Rules\ForbidReturnValueOfIncludeOnceRule; + +return static function (ContainerConfigurator $containerConfigurator): void { + $services = $containerConfigurator->services(); + $services->set(ForbidReturnValueOfIncludeOnceRule::class); +}; +``` + +:x: + +```php +class SomeClass +{ + public function run() + { + return require_once 'Test.php'; + } +} +``` + +
+ +## Boolish Methods has to have is/has/was Name + +- class: [`BoolishClassMethodPrefixRule`](src/Rules/BoolishClassMethodPrefixRule.php) + +```php +// ecs.php + +declare(strict_types=1); + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; +use Symplify\CodingStandard\Rules\BoolishClassMethodPrefixRule; + +return static function (ContainerConfigurator $containerConfigurator): void { + $services = $containerConfigurator->services(); + $services->set(BoolishClassMethodPrefixRule::class); +}; +``` + + +```php +class SomeClass +{ + public function old(): bool + { + return $this->age > 100; + } +} +``` + +:x: + +```php +class SomeClass +{ + public function isOld(): bool + { + return $this->age > 100; + } +} +``` + +:+1: + +
+ +## Constant type Must Match its Value + +- class: [`MatchingTypeConstantRule`](src/Rules/MatchingTypeConstantRule.php) + +```php +// ecs.php + +declare(strict_types=1); + +use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + +return static function (ContainerConfigurator $containerConfigurator): void { + $services = $containerConfigurator->services(); + $services->set(Symplify\CodingStandard\Rules\MatchingTypeConstantRule::class); +}; +``` + +```php +class SomeClass +{ + /** + * @var int + */ + private const LIMIT = 'max'; +} +``` + +:x: + + +```php +class SomeClass +{ + /** + * @var string + */ + private const LIMIT = 'max'; +} +``` + +:+1: + + +## Defined Method Argument should be Always Constant Value + +- class: [`ForceMethodCallArgumentConstantRule`](src/Rules/ForceMethodCallArgumentConstantRule.php) + +```yaml +# phpstan.neon +parameters: + symplify: + constant_arg_by_method_by_type: + AlwaysCallMeWithConstant: + some_type: [0] # positions + +rules: + - Symplify\CodingStandard\Rules\ForceMethodCallArgumentConstantRule +``` + +:x: + +```php +class SomeClass +{ + public function run() + { + $alwaysCallMeWithConstant = new AlwaysCallMeWithConstant(); + $alwaysCallMeWithConstant->call('someValue'); + // should be: $alwaysCallMeWithConstant->call(TypeList::SOME); + + } +} +``` + +
+ +## Require @see annotation to class Test case by Type + +- class: [`SeeAnnotationToTestRule`](src/Rules/SeeAnnotationToTestRule.php) + +```yaml +# phpstan.neon +parameters: + symplify: + required_see_types: + - PHPStan\Rules\Rule + +rules: + - Symplify\CodingStandard\Rules\SeeAnnotationToTestRule +``` + +:x: + +```php +use PHPStan\Rules\Rule; + +class SomeRule implements Rule +{ + // ... +} +``` + +:+1: + +```php +use PHPStan\Rules\Rule; + +/** + * @see SomeRuleTest + */ +class SomeRule implements Rule +{ + // ... +} +``` + +## Prefer Another Class + +- class: [`PreferredClassRule`](src/Rules/PreferredClassRule.php) + +```yaml +# phpstan.neon +parameters: + symplify: + old_to_preffered_classes: + DateTime: 'Nette\Utils\DateTime' + +rules: + - Symplify\CodingStandard\Rules\PreferredClassRule +``` + +:x: + +```php +$dateTime = new DateTime('now'); // should be "Nette\Utils\DateTime" +``` + +
+ +## Classes with Static Methods must have "Static" in the Name + +- class: [`Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule`](src/Rules/NoClassWithStaticMethodWithoutStaticNameRule.php) + +Be honest about static. [Why is static bad?](https://tomasvotruba.com/blog/2019/04/01/removing-static-there-and-back-again/) + +Value object static constructors, EventSubscriber and Command classe are excluded. + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule +``` + +:x: + +```php +class FormatConverter // should be: "StaticFormatConverter" +{ + public static function yamlToJson(array $yaml): array + { + // ... + } +} +``` + +
+ +## Use Unique Class Short Names + +- class: [`Symplify\CodingStandard\Rules\NoDuplicatedShortClassNameRule`](src/Rules/NoDuplicatedShortClassNameRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\Rules\NoDuplicatedShortClassNameRule +``` + +:x: + +```php +namespace App; + +class Finder +{ +} +``` + +```php +namespace App\Entity; + +class Finder // should be e.g. "EntityFinder" +{ +} +``` + +## Cognitive Complexity + +### Cognitive Complexity for Method and Class Must be Less than X + +- [Why it's the best rule in your coding standard?](https://www.tomasvotruba.com/blog/2018/05/21/is-your-code-readable-by-humans-cognitive-complexity-tells-you/) + +- class: [`FunctionLikeCognitiveComplexityRule`](packages/coding-standard/packages/cognitive-complexity/src/Rules/FunctionLikeCognitiveComplexityRule.php) +- class: [`ClassLikeCognitiveComplexityRule`](packages/coding-standard/packages/cognitive-complexity/src/Rules/ClassLikeCognitiveComplexityRule.php) + +```yaml +# phpstan.neon +includes: + - vendor/symplify/coding-standard/packages/cognitive-complexity/config/cognitive-complexity-rules.neon + +parameters: + symplify: + max_cognitive_complexity: 8 # default + max_class_cognitive_complexity: 50 # default +``` + +```php +class SomeClass +{ + public function simple($value) + { + if ($value !== 1) { + if ($value !== 2) { + if ($value !== 3) { + return false; + } + } + } + + return true; + } +} +``` + +:x: + +```php +class SomeClass +{ + public function simple($value) + { + if ($value === 1) { + return true; + } + + if ($value === 2) { + return true; + } + + return $value === 3; + } +} +``` + +:+1: + +## Object Calisthenics Rules + +### No `else` And `elseif` + +- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoElseAndElseIfRule`](packages/object-calisthenics/src/Rules/NoElseAndElseIfRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoElseAndElseIfRule +``` + +```php + + +### No Names Shorter than 3 Chars + +- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoShortNameRule`](packages/object-calisthenics/src/Rules/NoShortNameRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoShortNameRule +``` + +```php + + +### No Setter Methods + +- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoSetterClassMethodRule`](packages/coding-standard/src/Rules/ObjectCalisthenics/NoSetterClassMethodRule.php) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoSetterClassMethodRule +``` + +:x: + +```php +final class Person +{ + private string $name; + + public function setName(string $name) // should be "__construct" + { + $this->name = $name; + } +} +``` + +
+ +### No Chain Method Call + +- class: [`Symplify\CodingStandard\ObjectCalisthenics\Rules\NoChainMethodCallRule`](packages/coding-standard/src/Rules/ObjectCalisthenics/NoChainMethodCallRule.php) +- Check [Fluent Interfaces are Evil](https://ocramius.github.io/blog/fluent-interfaces-are-evil/) and [Fluent Interfaces Are Bad for Maintainability +](https://www.yegor256.com/2018/03/13/fluent-interfaces.html) + +```yaml +# phpstan.neon +rules: + - Symplify\CodingStandard\ObjectCalisthenics\Rules\NoChainMethodCallRule +``` + +```php +class SomeClass +{ + public function run() + { + return $this->create()->modify()->save(); + } +} +``` + +:x: + +```php +class SomeClass +{ + public function run() + { + $object = $this->create(); + $object->modify(); + $object->save(); + + return $object; + } +} +``` + +:+1: diff --git a/packages/monorepo-builder/src/DependencyUpdater.php b/packages/monorepo-builder/src/DependencyUpdater.php index d510354a84..b211d37d25 100644 --- a/packages/monorepo-builder/src/DependencyUpdater.php +++ b/packages/monorepo-builder/src/DependencyUpdater.php @@ -76,6 +76,7 @@ public function updateFileInfosWithVendorAndVersion( $packageComposerFileInfo, $shouldSkipCallable ); + $json = $this->processSection( $json, $vendor, diff --git a/packages/set-config-resolver/src/SetResolver.php b/packages/set-config-resolver/src/SetResolver.php index 43dc3802e4..7313947f8c 100644 --- a/packages/set-config-resolver/src/SetResolver.php +++ b/packages/set-config-resolver/src/SetResolver.php @@ -16,7 +16,7 @@ final class SetResolver /** * @var string[] */ - private $optionNames = []; + private $optionNames = OptionName::SET; /** * @var OptionValueResolver @@ -28,12 +28,8 @@ final class SetResolver */ private $setProvider; - /** - * @param string[] $optionNames - */ - public function __construct(SetProviderInterface $setProvider, array $optionNames = OptionName::SET) + public function __construct(SetProviderInterface $setProvider) { - $this->optionNames = $optionNames; $this->optionValueResolver = new OptionValueResolver(); $this->setProvider = $setProvider; } diff --git a/phpstan.neon b/phpstan.neon index a1163404e7..addce8b1a8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -3,6 +3,8 @@ includes: - vendor/slam/phpstan-extensions/conf/slam-rules.neon - vendor/slam/phpstan-extensions/conf/symfony-rules.neon - packages/coding-standard/config/symplify-rules.neon + # @todo resolve in next PR + # - packages/coding-standard/config/symplify-strict-rules.neon services: - From aae7a4fe6a92d5bb81880b87bb4518da9836a249 Mon Sep 17 00:00:00 2001 From: rector-bot Date: Tue, 18 Aug 2020 12:37:19 +0000 Subject: [PATCH 4/4] [rector] [CS] Split long god-README to smaller 3 files files - fixes, sniff and rules --- packages/set-config-resolver/src/SetResolver.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/set-config-resolver/src/SetResolver.php b/packages/set-config-resolver/src/SetResolver.php index 7313947f8c..9d20609b28 100644 --- a/packages/set-config-resolver/src/SetResolver.php +++ b/packages/set-config-resolver/src/SetResolver.php @@ -16,7 +16,7 @@ final class SetResolver /** * @var string[] */ - private $optionNames = OptionName::SET; + private const OPTION_NAMES = OptionName::SET; /** * @var OptionValueResolver @@ -36,7 +36,7 @@ public function __construct(SetProviderInterface $setProvider) public function detectFromInput(InputInterface $input): ?SmartFileInfo { - $setName = $this->optionValueResolver->getOptionValue($input, $this->optionNames); + $setName = $this->optionValueResolver->getOptionValue($input, self::OPTION_NAMES); if ($setName === null) { return null; }