diff --git a/README.md b/README.md index e414913..42df8e4 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,11 @@ Before create the PR or merge into develop, please run next commands for validat ```shell ./bin/phpunit +./bin/phpstan ./bin/phpcs --config-set show_warnings 0 -./bin/phpcs --standard=vendor/escapestudios/symfony2-coding-standard/Symfony/ src/ ./bin/phpcs --standard=tests/phpcs.xml tests/ +./bin/phpcs --standard=src/phpcs.xml src/ ``` diff --git a/src/PhpCs/FiveLab/ErrorCodes.php b/src/PhpCs/FiveLab/ErrorCodes.php index 9580076..12a51ef 100644 --- a/src/PhpCs/FiveLab/ErrorCodes.php +++ b/src/PhpCs/FiveLab/ErrorCodes.php @@ -34,4 +34,5 @@ final class ErrorCodes public const ARRAYS_DOC_VECTOR = 'ArraysDocVector'; public const PHPDOC_NOT_ALLOWED = 'PhpDocNotAllowed'; public const MISSED_SPACE = 'MissedSpace'; + public const MANY_SPACES = 'ManySpaces'; } diff --git a/src/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniff.php b/src/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniff.php new file mode 100644 index 0000000..9b6ea5b --- /dev/null +++ b/src/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniff.php @@ -0,0 +1,94 @@ +getTokens(); + $token = $tokens[$stackPtr]; + $content = $tokens[$stackPtr]['content']; + + // only spaces + if (!\preg_match('/^ +$/', $content)) { + return; + } + + $nextPtr = $phpcsFile->findNext(T_WHITESPACE, $stackPtr + 1, null, true); + $prevPtr = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); + + if (false === $prevPtr || \str_contains($tokens[$stackPtr - 1]['content'], \chr(10))) { + return; + } + + if (T_SEMICOLON !== $tokens[$nextPtr]['code'] && 1 === \strlen($token['content'])) { + return; + } + + if (T_SEMICOLON === $tokens[$nextPtr]['code']) { + $phpcsFile->addError( + 'Too many spaces. There must be no space before a semicolon.', + $stackPtr, + ErrorCodes::MANY_SPACES + ); + + return; + } + + if (T_DOUBLE_ARROW === $tokens[$nextPtr]['code']) { + return; + } + + if (T_MATCH_ARROW === $tokens[$nextPtr]['code']) { + return; + } + + if (T_CONST === $tokens[$stackPtr - 3]['code'] || T_CONST === $tokens[$stackPtr - 5]['code']) { + return; + } + + if (T_ENUM_CASE === $tokens[$stackPtr - 3]['code']) { + return; + } + + $functionPtr = $phpcsFile->findPrevious([T_FUNCTION, T_CLOSURE, T_FN], $stackPtr); + + if (\array_key_exists('parenthesis_opener', $tokens[$functionPtr]) && \array_key_exists('parenthesis_closer', $tokens[$functionPtr])) { + $open = $tokens[$functionPtr]['parenthesis_opener']; + $close = $tokens[$functionPtr]['parenthesis_closer']; + + if ($stackPtr > $open && $stackPtr < $close) { + return; + } + } + + $phpcsFile->addError( + 'Too many spaces. Must be one space.', + $stackPtr, + ErrorCodes::MANY_SPACES + ); + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniffTest.php b/tests/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniffTest.php new file mode 100644 index 0000000..7ae4887 --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Formatting/ManySpacesSniffTest.php @@ -0,0 +1,103 @@ + [ + __DIR__.'/Resources/many-spaces/success.php', + ], + + 'wrong' => [ + __DIR__.'/Resources/many-spaces/wrong.php', + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 3, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 4, + ], + [ + 'message' => 'Too many spaces. There must be no space before a semicolon.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 5, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 7, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 8, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 9, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 10, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 11, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 12, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 13, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 14, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 17, + ], + [ + 'message' => 'Too many spaces. Must be one space.', + 'source' => 'FiveLab.Formatting.ManySpaces.ManySpaces', + 'line' => 20, + ], + ], + ]; + } +} diff --git a/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/success.php b/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/success.php new file mode 100644 index 0000000..467431d --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/success.php @@ -0,0 +1,41 @@ + $b) { +} + +$ar = [ + 'a' => 8, + 'bla' => 5, + 'fddfdf0' => 65, +]; + +return [ + 'success' => [], + 'bba' => 1, +]; + +$ds = match('dfdfdf0') { + 'ffd' => 'ffd', + 'fddddd' => 'ddd', +}; diff --git a/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/wrong.php b/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/wrong.php new file mode 100644 index 0000000..7184790 --- /dev/null +++ b/tests/PhpCs/FiveLab/Sniffs/Formatting/Resources/many-spaces/wrong.php @@ -0,0 +1,20 @@ + $b) {} +if ($a === $b) {} +if ($a > $b) {} +if ($a === $b) {} +if ($a && $b) {} +if ($a || $b) {} +if ($a && $b) {} +if ($a || $b) {} + +$arr = [ + $d => 1 +]; + +return $a;