diff --git a/src/Cdn77/Sniffs/NamingConventions/ValidConstantNameSniff.php b/src/Cdn77/Sniffs/NamingConventions/ValidConstantNameSniff.php new file mode 100644 index 0000000..a08b3c2 --- /dev/null +++ b/src/Cdn77/Sniffs/NamingConventions/ValidConstantNameSniff.php @@ -0,0 +1,142 @@ + + */ + public function register(): array + { + return [ + T_STRING, + T_CONST, + ]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @param File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token in the stack passed in $tokens. + * + * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint + */ + public function process(File $phpcsFile, $stackPtr): void + { + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr]['code'] === T_CONST) { + // This is a class constant. + $constant = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); + if ($constant === false) { + return; + } + + $constName = $tokens[$constant]['content']; + + if ($this->matchesRegex($constName, $this->pattern)) { + return; + } + + $error = sprintf('Constant "%%s" does not match pattern "%s"', $this->pattern); + $data = [$constName]; + $phpcsFile->addError( + $error, + $constant, + self::CodeClassConstantNotMatchPattern, + $data, + ); + } + + // Only interested in define statements now. + if (strtolower($tokens[$stackPtr]['content']) !== 'define') { + return; + } + + // Make sure this is not a method call. + $prev = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true); + if ( + $tokens[$prev]['code'] === T_OBJECT_OPERATOR + || $tokens[$prev]['code'] === T_DOUBLE_COLON + || $tokens[$prev]['code'] === T_NULLSAFE_OBJECT_OPERATOR + ) { + return; + } + + // If the next non-whitespace token after this token + // is not an opening parenthesis then it is not a function call. + $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, $stackPtr + 1, null, true); + if ($openBracket === false) { + return; + } + + // The next non-whitespace token must be the constant name. + $constPtr = $phpcsFile->findNext(T_WHITESPACE, $openBracket + 1, null, true); + if ($tokens[$constPtr]['code'] !== T_CONSTANT_ENCAPSED_STRING) { + return; + } + + $constName = $tokens[$constPtr]['content']; + + // Check for constants like self::CONSTANT. + $prefix = ''; + $splitPos = strpos($constName, '::'); + if ($splitPos !== false) { + $prefix = substr($constName, 0, $splitPos + 2); + $constName = substr($constName, $splitPos + 2); + } + + // Strip namespace from constant like /foo/bar/CONSTANT. + $splitPos = strrpos($constName, '\\'); + if ($splitPos !== false) { + $prefix = substr($constName, 0, $splitPos + 1); + $constName = substr($constName, $splitPos + 1); + } + + if ($this->matchesRegex($constName, $this->pattern)) { + return; + } + + $error = sprintf('Constant "%%s" does not match pattern "%s"', $this->pattern); + $data = [ + $prefix . $constName, + ]; + $phpcsFile->addError($error, $stackPtr, self::CodeConstantNotMatchPattern, $data); + } + + private function matchesRegex(string $varName, string $pattern): bool + { + return preg_match(sprintf('~%s~', $pattern), $varName) === 1; + } +} diff --git a/src/Cdn77/Sniffs/NamingConventions/ValidVariableNameSniff.php b/src/Cdn77/Sniffs/NamingConventions/ValidVariableNameSniff.php index 436a4ce..6133500 100644 --- a/src/Cdn77/Sniffs/NamingConventions/ValidVariableNameSniff.php +++ b/src/Cdn77/Sniffs/NamingConventions/ValidVariableNameSniff.php @@ -22,15 +22,15 @@ class ValidVariableNameSniff extends AbstractVariableSniff { - public const CODE_DOES_NOT_MATCH_PATTERN = 'DoesNotMatchPattern'; - public const CODE_MEMBER_DOES_NOT_MATCH_PATTERN = 'MemberDoesNotMatchPattern'; - public const CODE_STRING_DOES_NOT_MATCH_PATTERN = 'StringDoesNotMatchPattern'; - private const PATTERN_CAMEL_CASE = '\b([a-zA-Z][a-zA-Z0-9]*?([A-Z][a-zA-Z0-9]*?)*?)\b'; - private const PATTERN_CAMEL_CASE_OR_UNUSED = '\b(([a-zA-Z][a-zA-Z0-9]*?([A-Z][a-zA-Z0-9]*?)*?)|_+)\b'; + public const CodeDoesNotMatchPattern = 'DoesNotMatchPattern'; + public const CodeMemberDoesNotMatchPattern = 'MemberDoesNotMatchPattern'; + public const CodeStringDoesNotMatchPattern = 'StringDoesNotMatchPattern'; + private const PatternCamelCase = '\b([a-zA-Z][a-zA-Z0-9]*?([A-Z][a-zA-Z0-9]*?)*?)\b'; + private const PatternCamelCaseOrUnused = '\b(([a-zA-Z][a-zA-Z0-9]*?([A-Z][a-zA-Z0-9]*?)*?)|_+)\b'; - public string $pattern = self::PATTERN_CAMEL_CASE_OR_UNUSED; - public string $memberPattern = self::PATTERN_CAMEL_CASE; - public string $stringPattern = self::PATTERN_CAMEL_CASE; + public string $pattern = self::PatternCamelCaseOrUnused; + public string $memberPattern = self::PatternCamelCase; + public string $stringPattern = self::PatternCamelCase; /** * Processes this test, when one of its tokens is encountered. @@ -69,7 +69,7 @@ protected function processVariable(File $phpcsFile, $stackPtr): void if (! $this->matchesRegex($objVarName, $this->memberPattern)) { $error = sprintf('Member variable "%%s" does not match pattern "%s"', $this->memberPattern); $data = [$objVarName]; - $phpcsFile->addError($error, $var, self::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, $data); + $phpcsFile->addError($error, $var, self::CodeMemberDoesNotMatchPattern, $data); } } } @@ -80,7 +80,7 @@ protected function processVariable(File $phpcsFile, $stackPtr): void if (! $this->matchesRegex($varName, $this->memberPattern)) { $error = sprintf('Member variable "%%s" does not match pattern "%s"', $this->memberPattern); $data = [$tokens[$stackPtr]['content']]; - $phpcsFile->addError($error, $stackPtr, self::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, $data); + $phpcsFile->addError($error, $stackPtr, self::CodeMemberDoesNotMatchPattern, $data); } return; @@ -92,7 +92,7 @@ protected function processVariable(File $phpcsFile, $stackPtr): void $error = sprintf('Variable "%%s" does not match pattern "%s"', $this->pattern); $data = [$varName]; - $phpcsFile->addError($error, $stackPtr, self::CODE_DOES_NOT_MATCH_PATTERN, $data); + $phpcsFile->addError($error, $stackPtr, self::CodeDoesNotMatchPattern, $data); } /** @@ -124,7 +124,7 @@ protected function processMemberVar(File $phpcsFile, $stackPtr): void } $error = sprintf('Member variable "%%s" does not match pattern "%s"', $this->memberPattern); - $phpcsFile->addError($error, $stackPtr, self::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, $errorData); + $phpcsFile->addError($error, $stackPtr, self::CodeMemberDoesNotMatchPattern, $errorData); } /** @@ -161,7 +161,7 @@ protected function processVariableInString(File $phpcsFile, $stackPtr): void $error = sprintf('Variable "%%s" does not match pattern "%s"', $this->stringPattern); $data = [$varName]; - $phpcsFile->addError($error, $stackPtr, self::CODE_STRING_DOES_NOT_MATCH_PATTERN, $data); + $phpcsFile->addError($error, $stackPtr, self::CodeStringDoesNotMatchPattern, $data); } } diff --git a/src/Cdn77/ruleset.xml b/src/Cdn77/ruleset.xml index a932fda..b9ece02 100644 --- a/src/Cdn77/ruleset.xml +++ b/src/Cdn77/ruleset.xml @@ -26,6 +26,9 @@ + + + @@ -39,6 +42,7 @@ + diff --git a/tests/Sniffs/NamingConventions/ValidConstantNameSniffTest.php b/tests/Sniffs/NamingConventions/ValidConstantNameSniffTest.php new file mode 100644 index 0000000..e652cc0 --- /dev/null +++ b/tests/Sniffs/NamingConventions/ValidConstantNameSniffTest.php @@ -0,0 +1,42 @@ + ValidConstantNameSniff::CodeConstantNotMatchPattern, + 8 => ValidConstantNameSniff::CodeConstantNotMatchPattern, + 10 => ValidConstantNameSniff::CodeConstantNotMatchPattern, + 11 => ValidConstantNameSniff::CodeConstantNotMatchPattern, + 17 => ValidConstantNameSniff::CodeClassConstantNotMatchPattern, + 18 => ValidConstantNameSniff::CodeClassConstantNotMatchPattern, + 19 => ValidConstantNameSniff::CodeClassConstantNotMatchPattern, + ]; + $possibleLines = array_keys($errorTypesPerLine); + + $errors = $file->getErrors(); + foreach ($errors as $line => $error) { + self::assertContains($line, $possibleLines, json_encode($error, JSON_THROW_ON_ERROR)); + + $errorType = $errorTypesPerLine[$line]; + + self::assertSniffError($file, $line, $errorType); + } + + self::assertSame(6, $file->getErrorCount()); + } +} diff --git a/tests/Sniffs/NamingConventions/ValidVariableNameSniffTest.php b/tests/Sniffs/NamingConventions/ValidVariableNameSniffTest.php index 440e92c..ee71629 100644 --- a/tests/Sniffs/NamingConventions/ValidVariableNameSniffTest.php +++ b/tests/Sniffs/NamingConventions/ValidVariableNameSniffTest.php @@ -19,47 +19,47 @@ public function testErrors(): void $file = self::checkFile(__DIR__ . '/data/ValidVariableNameSniffTest.inc'); $errorTypesPerLine = [ - 3 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 5 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 10 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 12 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 15 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 17 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 19 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 20 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 21 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 26 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 28 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 31 => ValidVariableNameSniff::CODE_STRING_DOES_NOT_MATCH_PATTERN, - 32 => ValidVariableNameSniff::CODE_STRING_DOES_NOT_MATCH_PATTERN, - 34 => ValidVariableNameSniff::CODE_STRING_DOES_NOT_MATCH_PATTERN, - 37 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 39 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 48 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 50 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 53 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 55 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 57 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 58 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 59 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 62 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 76 => ValidVariableNameSniff::CODE_STRING_DOES_NOT_MATCH_PATTERN, - 100 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 101 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 102 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 117 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 118 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 128 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 132 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 134 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 135 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - 140 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, - 142 => ValidVariableNameSniff::CODE_MEMBER_DOES_NOT_MATCH_PATTERN, + 3 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 5 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 10 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 12 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 15 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 17 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 19 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 20 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 21 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 26 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 28 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 31 => ValidVariableNameSniff::CodeStringDoesNotMatchPattern, + 32 => ValidVariableNameSniff::CodeStringDoesNotMatchPattern, + 34 => ValidVariableNameSniff::CodeStringDoesNotMatchPattern, + 37 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 39 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 48 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 50 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 53 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 55 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 57 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 58 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 59 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 62 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 76 => ValidVariableNameSniff::CodeStringDoesNotMatchPattern, + 100 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 101 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 102 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 117 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 118 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 128 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 132 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 134 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 135 => ValidVariableNameSniff::CodeDoesNotMatchPattern, + 140 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, + 142 => ValidVariableNameSniff::CodeMemberDoesNotMatchPattern, 144 => [ - ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, - ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, + ValidVariableNameSniff::CodeDoesNotMatchPattern, + ValidVariableNameSniff::CodeDoesNotMatchPattern, ], - 146 => ValidVariableNameSniff::CODE_DOES_NOT_MATCH_PATTERN, + 146 => ValidVariableNameSniff::CodeDoesNotMatchPattern, ]; $possibleLines = array_keys($errorTypesPerLine); diff --git a/tests/Sniffs/NamingConventions/data/ValidConstantNameTest.inc b/tests/Sniffs/NamingConventions/data/ValidConstantNameTest.inc new file mode 100644 index 0000000..a8a3ae8 --- /dev/null +++ b/tests/Sniffs/NamingConventions/data/ValidConstantNameTest.inc @@ -0,0 +1,24 @@ +define('bar'); +$foo->getBar()->define('foo'); +Foo::define('bar');