diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index 3de090d..cc486ff 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -38,5 +38,8 @@ jobs: - name: phpcs version run: vendor/bin/phpcs --version + - name: Validate code style + run: vendor/bin/phpcs -w -p -s --standard=ruleset.xml --ignore="tests/*not-allowed*" custom-standards/ tests/ + - name: Run test suite run: php tests/runner.php diff --git a/Makefile b/Makefile index bfc2cc0..135d4ec 100644 --- a/Makefile +++ b/Makefile @@ -4,17 +4,16 @@ COMPOSER_BIN=$(PHP_BIN) composer.phar # --------------------------------------------- # make -.DEFAULT_GOAL := install-dev +.DEFAULT_GOAL := install -# make test -test: - $(PHP_BIN) tests/runner.php - -# make install install: + $(COMPOSER_BIN) install -# --------------------------------------------- -# functions +sniff: + $(PHP_BIN) vendor/bin/phpcs -w -p -s --standard=ruleset.xml --ignore="tests/*not-allowed*" custom-standards/ tests/ -install-dev: - $(COMPOSER_BIN) install +sniff-fix: + $(PHP_BIN) vendor/bin/phpcbf -w -p -s --standard=ruleset.xml --ignore="tests/*not-allowed*" custom-standards/ tests/ + +test: + $(PHP_BIN) tests/runner.php diff --git a/README.md b/README.md index 807fcce..8d46bb4 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,13 @@ interpreted as described in [RFC 2119](http://www.ietf.org/rfc/rfc2119.txt). To prepare run command: ```bash -make +make install +``` + +To check code style compliance or to fix what can be autofixed run commands: +```bash +make sniff +make sniff-fix ``` To test ruleset run command: diff --git a/custom-standards/Flyeralarm/Sniffs/Classes/FullyQualifiedSniff.php b/custom-standards/Flyeralarm/Sniffs/Classes/FullyQualifiedSniff.php index 1cbaf6d..74c547a 100644 --- a/custom-standards/Flyeralarm/Sniffs/Classes/FullyQualifiedSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/Classes/FullyQualifiedSniff.php @@ -13,7 +13,7 @@ class FullyQualifiedSniff implements Sniff */ public function register() { - return array(T_DOUBLE_COLON, T_NEW, T_EXTENDS, T_IMPLEMENTS); + return [T_DOUBLE_COLON, T_NEW, T_EXTENDS, T_IMPLEMENTS]; } /** diff --git a/custom-standards/Flyeralarm/Sniffs/ControlStructures/YodaSniff.php b/custom-standards/Flyeralarm/Sniffs/ControlStructures/YodaSniff.php index 3f0cf28..2a63a02 100644 --- a/custom-standards/Flyeralarm/Sniffs/ControlStructures/YodaSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/ControlStructures/YodaSniff.php @@ -12,7 +12,7 @@ class YodaSniff implements Sniff */ public function register() { - return array(T_IF, T_ELSEIF, T_WHILE); + return [T_IF, T_ELSEIF, T_WHILE]; } /** @@ -87,21 +87,25 @@ private function checkConditionalOrderForArithmeticExpression( // e.g. if ($foo = true) // e.g. if ($foo = 'bar') - if (in_array($leftOperandTokenId, $functionAndVariableTokenIds) + if ( + in_array($leftOperandTokenId, $functionAndVariableTokenIds) && in_array($rightOperandTokenId, $languageTypeTokenIds) ) { return; } // e.g. if (count(..) > $test) // e.g. if ($foo == $bar) - if (in_array($leftOperandTokenId, $functionAndVariableTokenIds) + if ( + in_array($leftOperandTokenId, $functionAndVariableTokenIds) && in_array($rightOperandTokenId, $functionAndVariableTokenIds) ) { return; } // e.g. if ('foo' == 'bar') - if (in_array($leftOperandTokenId, $languageTypeTokenIds) - && in_array($rightOperandTokenId, $languageTypeTokenIds)) { + if ( + in_array($leftOperandTokenId, $languageTypeTokenIds) + && in_array($rightOperandTokenId, $languageTypeTokenIds) + ) { return; } diff --git a/custom-standards/Flyeralarm/Sniffs/Docblock/ExpectedExceptionMessageSniff.php b/custom-standards/Flyeralarm/Sniffs/Docblock/ExpectedExceptionMessageSniff.php index 1c5ca32..ef55a3a 100644 --- a/custom-standards/Flyeralarm/Sniffs/Docblock/ExpectedExceptionMessageSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/Docblock/ExpectedExceptionMessageSniff.php @@ -12,7 +12,7 @@ class ExpectedExceptionMessageSniff implements Sniff */ public function register() { - return array(T_DOC_COMMENT_OPEN_TAG); + return [T_DOC_COMMENT_OPEN_TAG]; } /** @@ -25,8 +25,10 @@ public function process(File $phpcsFile, $stackPtr) if (!$this->hasAnnotationInDoc($phpcsFile, $stackPtr, '@expectedException')) { return; } - if ($this->hasAnnotationInDoc($phpcsFile, $stackPtr, '@expectedExceptionMessage') - || $this->hasAnnotationInDoc($phpcsFile, $stackPtr, '@expectedExceptionMessageRegExp')) { + if ( + $this->hasAnnotationInDoc($phpcsFile, $stackPtr, '@expectedExceptionMessage') + || $this->hasAnnotationInDoc($phpcsFile, $stackPtr, '@expectedExceptionMessageRegExp') + ) { return; } diff --git a/custom-standards/Flyeralarm/Sniffs/File/ExceptionMessageSniff.php b/custom-standards/Flyeralarm/Sniffs/File/ExceptionMessageSniff.php index a8193d7..443220f 100644 --- a/custom-standards/Flyeralarm/Sniffs/File/ExceptionMessageSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/File/ExceptionMessageSniff.php @@ -2,6 +2,7 @@ namespace Flyeralarm\CodingGuidelines\Flyeralarm\Sniffs\File; +use PHP_CodeSniffer\Exceptions\RuntimeException; use PHP_CodeSniffer\Sniffs\Sniff; use PHP_CodeSniffer\Files\File; @@ -12,26 +13,26 @@ class ExceptionMessageSniff implements Sniff */ public function register() { - return array(T_CLASS); + return [T_CLASS]; } /** * @param File $phpcsFile * @param int $stackPtr - * @return int|void - * @throws \PHP_CodeSniffer\Exceptions\RuntimeException + * @return void + * @throws RuntimeException */ public function process(File $phpcsFile, $stackPtr) { $className = $phpcsFile->getDeclarationName($stackPtr); - if (strpos($className, 'Exception') === false) { + if (! $this->doesStringEndsWith($className, 'Exception')) { return; } $tokens = $phpcsFile->getTokens(); $ptr = -1; - while($ptr = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $ptr + 1)) { + while ($ptr = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $ptr + 1)) { if (strpos($tokens[$ptr]['content'], '!') !== false) { $phpcsFile->addError( 'Exclamationmarks are not allowed in Exceptionmessages', @@ -49,4 +50,13 @@ public function process(File $phpcsFile, $stackPtr) } } } + + /** + * @alias str_ends_with in PHP8+ + */ + public function doesStringEndsWith(string $className, string $suffix): bool + { + $suffixLength = strlen($suffix); + return ($suffixLength === 0 || 0 === substr_compare($className, $suffix, - $suffixLength)); + } } diff --git a/custom-standards/Flyeralarm/Sniffs/File/ForbiddenKeywordsSniff.php b/custom-standards/Flyeralarm/Sniffs/File/ForbiddenKeywordsSniff.php index efbf0c5..e2e3da5 100644 --- a/custom-standards/Flyeralarm/Sniffs/File/ForbiddenKeywordsSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/File/ForbiddenKeywordsSniff.php @@ -12,7 +12,7 @@ class ForbiddenKeywordsSniff implements Sniff */ public function register() { - return array(T_CLASS, T_ABSTRACT, T_TRAIT); + return [T_CLASS, T_ABSTRACT, T_TRAIT]; } /** diff --git a/custom-standards/Flyeralarm/Sniffs/File/NamespacesSniff.php b/custom-standards/Flyeralarm/Sniffs/File/NamespacesSniff.php index 01cd9a1..a351d8f 100644 --- a/custom-standards/Flyeralarm/Sniffs/File/NamespacesSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/File/NamespacesSniff.php @@ -12,7 +12,7 @@ class NamespacesSniff implements Sniff */ public function register() { - return array(T_CLASS, T_ABSTRACT, T_TRAIT, T_INTERFACE); + return [T_CLASS, T_ABSTRACT, T_TRAIT, T_INTERFACE]; } /** @@ -24,7 +24,7 @@ public function process(File $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $ptr = -1; - while($ptr = $phpcsFile->findNext(T_NS_SEPARATOR, $ptr + 1)) { + while ($ptr = $phpcsFile->findNext(T_NS_SEPARATOR, $ptr + 1)) { if (strpos($tokens[$ptr + 1]['content'], '_') !== false) { $phpcsFile->addError( 'Using underscore within namespaces is discouraged', diff --git a/custom-standards/Flyeralarm/Sniffs/File/NoClassKindSuffixSniff.php b/custom-standards/Flyeralarm/Sniffs/File/NoClassKindSuffixSniff.php index 0e98b85..474371a 100644 --- a/custom-standards/Flyeralarm/Sniffs/File/NoClassKindSuffixSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/File/NoClassKindSuffixSniff.php @@ -12,7 +12,7 @@ class NoClassKindSuffixSniff implements Sniff */ public function register() { - return array(T_INTERFACE, T_CLASS, T_TRAIT); + return [T_INTERFACE, T_CLASS, T_TRAIT]; } /** diff --git a/custom-standards/Flyeralarm/Sniffs/Variable/LowerCamelCaseSniff.php b/custom-standards/Flyeralarm/Sniffs/Variable/LowerCamelCaseSniff.php index 46d016b..a8a48d0 100644 --- a/custom-standards/Flyeralarm/Sniffs/Variable/LowerCamelCaseSniff.php +++ b/custom-standards/Flyeralarm/Sniffs/Variable/LowerCamelCaseSniff.php @@ -28,7 +28,7 @@ class LowerCamelCaseSniff implements Sniff */ public function register() { - return array(T_OPEN_TAG); + return [T_OPEN_TAG]; } /** diff --git a/tests/TestRunner.php b/tests/TestRunner.php new file mode 100644 index 0000000..d62736b --- /dev/null +++ b/tests/TestRunner.php @@ -0,0 +1,76 @@ +recursiveDirProcess($dirPath); + if ($hasError) { + exit(1); + } + exit(0); + } + + public function recursiveDirProcess(string $dirPath, bool $hasError = false): bool + { + $dir = opendir($dirPath); + while (($file = readdir($dir)) !== false) { + if (strpos($file, '.') === 0) { + continue; + } + if (is_dir($dirPath . $file)) { + $hasError = $this->recursiveDirProcess($dirPath . $file . '/', $hasError); + continue; + } + + $fileContent = file_get_contents($dirPath . $file); + $snifferOutput = shell_exec( + sprintf( + "%s -w -p -s --report-width=120 --standard=%s %s", + escapeshellcmd(__DIR__ . '/../vendor/bin/phpcs'), + escapeshellarg(__DIR__ . '/ruleset.xml'), + escapeshellarg($dirPath . $file) + ) + ); + + // expectedPass + if (preg_match('|//\s@expectedPass$|m', $fileContent)) { + if (preg_match('|^FOUND.*AFFECTING.*LINE|m', $snifferOutput) === 0) { + echo 'OK - [' . $dirPath . $file . ']' . PHP_EOL; + continue; + } + + $hasError = true; + echo "ERROR - [" . $dirPath . $file . "]:' + . ' Test was expected to fully pass. Result: " . PHP_EOL . $snifferOutput . PHP_EOL; + continue; + } + + // expectedError + preg_match('|//\s@expectedError\s(.*)$|m', $fileContent, $expectedMatch); + if (count($expectedMatch) !== 2) { + echo 'WARNING - [' . $dirPath . $file . ']:' + . ' File must contain exactly one "@expectedError "' + . ' or "@expectedPass" comment' . PHP_EOL; + continue; + } + $expected = $expectedMatch[1]; + if (preg_match('/ERROR\s\|\s?[\[\]x\s]*\s' . preg_quote($expected, '/') . '/', $snifferOutput) === 0) { + $hasError = true; + echo 'ERROR - [' . $dirPath . $file . ']:' + . ' Expectation <<' . $expected . '>>' + . ' not found in result: ' . PHP_EOL . $snifferOutput . PHP_EOL; + } else { + echo 'OK - [' . $dirPath . $file . ']' . PHP_EOL; + } + } + return $hasError; + } +} diff --git a/tests/rules/array/allowed/ShortArraySyntax.php b/tests/rules/array/allowed/ShortArraySyntax.php index 145fb42..929d60d 100644 --- a/tests/rules/array/allowed/ShortArraySyntax.php +++ b/tests/rules/array/allowed/ShortArraySyntax.php @@ -1,4 +1,5 @@ " or "@expectedPass" comment' . PHP_EOL; - continue; - } - $expected = $expectedMatch[1]; - if (preg_match('/ERROR\s\|\s?[\[\]x\s]*\s' . preg_quote($expected, '/') . '/', $snifferOutput) === 0) { - $hasError = true; - echo 'ERROR - [' . $dirPath . $file . ']: Expectation <<' . $expected . '>> not found in result: ' . PHP_EOL . $snifferOutput . PHP_EOL; - } else { - echo 'OK - [' . $dirPath . $file . ']' . PHP_EOL; - } - } -} +(new \Flyeralarm\Sniffer\Tests\TestRunner())->processDir(__DIR__ . '/rules/');