From dd0815fb524bda4d76f4e2d5915d0aa4327d0e55 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Thu, 16 Oct 2025 14:42:59 +0200 Subject: [PATCH 01/30] refactor(phpcs): Upgrade to 4.x in composer --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 5858f899..18661bef 100644 --- a/composer.json +++ b/composer.json @@ -17,9 +17,9 @@ "php": ">=7.2", "ext-mbstring": "*", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1 || ^1.0.0", - "sirbrillig/phpcs-variable-analysis": "^2.11.7", - "slevomat/coding-standard": "^8.11", - "squizlabs/php_codesniffer": "^3.13", + "sirbrillig/phpcs-variable-analysis": "^2.13", + "slevomat/coding-standard": "^8.24", + "squizlabs/php_codesniffer": "^4.0", "symfony/yaml": ">=3.4.0" }, "autoload": { From 6a1ab873b21581c795af61d3f1c43cfe59bae238 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 10:33:55 +0200 Subject: [PATCH 02/30] fork our own get sniff code method --- tests/Drupal/CoderSniffUnitTest.php | 40 ++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/tests/Drupal/CoderSniffUnitTest.php b/tests/Drupal/CoderSniffUnitTest.php index 4210b88b..d062db3c 100644 --- a/tests/Drupal/CoderSniffUnitTest.php +++ b/tests/Drupal/CoderSniffUnitTest.php @@ -62,8 +62,6 @@ abstract class CoderSniffUnitTest extends TestCase */ public function setUp(): void { - $class = get_class($this); - $this->rootDir = __DIR__.'/../../'; $this->testsDir = __DIR__.'/'; // Required to pull in all the defines from the tokens file. @@ -140,7 +138,7 @@ final public function testSniff() $this->markTestSkipped(); } - $sniffCode = Common::getSniffCode(get_class($this)); + $sniffCode = $this->getSniffCode(static::class); list($standardName, $categoryName, $sniffName) = explode('.', $sniffCode); // In the case where we are running all the sniffs, the standard will @@ -422,6 +420,42 @@ public function generateFailureMessages(LocalFile $file): array }//end generateFailureMessages() + /** + * Given a test class name, returns the code for the sniff. + * + * Forked from PHP_CodeSniffer\Util\Common to work with our test files. + * + * @param string $testClass The fully qualified test class name. + * + * @return string + * + * @throws \InvalidArgumentException When $testClass is not a non-empty string. + * @throws \InvalidArgumentException When $testClass is not a valid FQN for a test class. + */ + public function getSniffCode(string $testClass): string + { + if (is_string($testClass) === false || $testClass === '') { + throw new \InvalidArgumentException('The $testClass parameter must be a non-empty string'); + } + + $parts = explode('\\', $testClass); + $partsCount = count($parts); + + $sniff = $parts[($partsCount - 1)]; + + if ($sniff !== 'UnitTest' && substr($sniff, -8) === 'UnitTest') { + // Unit test class name. + $sniff = substr($sniff, 0, -8); + } else { + throw new \InvalidArgumentException( + 'The $testClass parameter was not passed a fully qualified sniff(test) class name. Received: ' . $testClass + ); + } + + $standard = $parts[($partsCount - 4)]; + $category = $parts[($partsCount - 2)]; + return $standard . '.' . $category . '.' . $sniff; + } /** * Set a list of CLI values before the file is tested. From d9a8bfe9c3c8456dccbc9051bb8929347cbc3830 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 10:50:36 +0200 Subject: [PATCH 03/30] property and tokenizer type fixes --- .../Drupal/Sniffs/Commenting/FileCommentSniff.php | 14 +++++--------- .../Sniffs/Commenting/InlineCommentSniff.php | 1 - 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php index 72b490d4..78b6abae 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php @@ -138,16 +138,12 @@ public function process(File $phpcsFile, $stackPtr) if ($fix === true) { // Only PHP has a real opening tag, additional newline at the // beginning here. - if ($phpcsFile->tokenizerType === 'PHP') { - // In templates add the file doc block to the very beginning of - // the file. - if ($tokens[0]['code'] === T_INLINE_HTML) { - $phpcsFile->fixer->addContentBefore(0, "\n"); - } else { - $phpcsFile->fixer->addContent($stackPtr, "\n/**\n * @file\n */\n"); - } + // In templates add the file doc block to the very beginning of + // the file. + if ($tokens[0]['code'] === T_INLINE_HTML) { + $phpcsFile->fixer->addContentBefore(0, "\n"); } else { - $phpcsFile->fixer->addContent($stackPtr, "/**\n * @file\n */\n"); + $phpcsFile->fixer->addContent($stackPtr, "\n/**\n * @file\n */\n"); } } diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/InlineCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/InlineCommentSniff.php index 49c77160..74415b79 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/InlineCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/InlineCommentSniff.php @@ -83,7 +83,6 @@ public function process(File $phpcsFile, $stackPtr) T_STATIC, T_ABSTRACT, T_CONST, - T_PROPERTY, T_INCLUDE, T_INCLUDE_ONCE, T_REQUIRE, From 869cb6ee222f1746e56abbdd65f2d506c06e3b04 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 11:05:59 +0200 Subject: [PATCH 04/30] type hint --- tests/Drupal/CoderSniffUnitTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Drupal/CoderSniffUnitTest.php b/tests/Drupal/CoderSniffUnitTest.php index d062db3c..a2c7851e 100644 --- a/tests/Drupal/CoderSniffUnitTest.php +++ b/tests/Drupal/CoderSniffUnitTest.php @@ -87,7 +87,7 @@ public function setUp(): void * * @return array */ - protected function getTestFiles($testFileBase): array + protected function getTestFiles(string $testFileBase): array { $testFiles = []; From a86e689799979a71aed6a5983b2635c02ebcf151 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 12:02:37 +0200 Subject: [PATCH 05/30] fix namespace detection --- coder_sniffer/DrupalPractice/Project.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/coder_sniffer/DrupalPractice/Project.php b/coder_sniffer/DrupalPractice/Project.php index c7d6a6a5..d8322e47 100644 --- a/coder_sniffer/DrupalPractice/Project.php +++ b/coder_sniffer/DrupalPractice/Project.php @@ -195,18 +195,8 @@ public static function isServiceClass(File $phpcsFile, $classPtr) $cache[$phpcsFile->getFilename()] = false; return false; } - - $nsEnd = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - T_WHITESPACE, - ], - ($namespacePtr + 1), - null, - true - ); - $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); + $nameQualifiedPtr = $phpcsFile->findNext(T_NAME_QUALIFIED, ($namespacePtr + 1)); + $namespace = $phpcsFile->getTokens()[$nameQualifiedPtr]['content'] ?? ''; $classNameSpaced = ltrim($namespace.'\\'.$phpcsFile->getDeclarationName($classPtr), '\\'); foreach ($services['services'] as $service) { From f80f514c5d54a6277ac83e62d3dae3bfb242ba54 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 12:12:43 +0200 Subject: [PATCH 06/30] fix GlobalDrupal sniff --- .../DrupalPractice/Sniffs/Objects/GlobalDrupalSniff.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalDrupalSniff.php b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalDrupalSniff.php index 4148ba8e..91245f85 100644 --- a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalDrupalSniff.php +++ b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalDrupalSniff.php @@ -51,7 +51,7 @@ class GlobalDrupalSniff implements Sniff */ public function register() { - return [T_STRING]; + return [T_NAME_FULLY_QUALIFIED]; }//end register() @@ -71,7 +71,7 @@ public function process(File $phpcsFile, $stackPtr) // We are only interested in Drupal:: static method calls, not in the global // scope. - if ($tokens[$stackPtr]['content'] !== 'Drupal' + if ($tokens[$stackPtr]['content'] !== '\Drupal' || $tokens[($stackPtr + 1)]['code'] !== T_DOUBLE_COLON || isset($tokens[($stackPtr + 2)]) === false || $tokens[($stackPtr + 2)]['code'] !== T_STRING From 469b1724321da2b7a33d9d06eff9a45b0d3bae21 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 12:40:09 +0200 Subject: [PATCH 07/30] fix global class sniff --- .../Sniffs/Objects/GlobalClassSniff.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php index f84c74e5..b94011e8 100644 --- a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php +++ b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php @@ -182,11 +182,16 @@ protected function getFullyQualifiedName(File $phpcsFile, $className) $useStatement = $phpcsFile->findNext(T_USE, 0); while ($useStatement !== false) { $endPtr = $phpcsFile->findEndOfStatement($useStatement); - $useEnd = ($phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], ($useStatement + 1), null, true) - 1); - $useFullName = trim($phpcsFile->getTokensAsString(($useStatement + 1), ($useEnd - $useStatement)), '\\ '); + $useFullNamePtr = $phpcsFile->findNext([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], ($useStatement + 1), $endPtr); + if ($useFullNamePtr === false) { + // No qualified name found, skip to next use statement. + $useStatement = $phpcsFile->findNext(T_USE, ($endPtr + 1)); + continue; + } + $useFullName = trim($phpcsFile->getTokens()[$useFullNamePtr]['content'], '\\ '); // Check if use statement contains an alias. - $asPtr = $phpcsFile->findNext(T_AS, ($useEnd + 1), $endPtr); + $asPtr = $phpcsFile->findNext(T_AS, ($useFullNamePtr + 1), $endPtr); if ($asPtr !== false) { $aliasName = trim($phpcsFile->getTokensAsString(($asPtr + 1), ($endPtr - 1 - $asPtr))); if ($aliasName === $className) { From fecc52050ad5bf4615fe19f9d782af4364a5229b Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 13:17:58 +0200 Subject: [PATCH 08/30] class create sniff --- .../Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php index 7d067fe6..cb12221a 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php @@ -68,7 +68,9 @@ public function process(File $phpcsFile, $stackPtr) // "new \DOMDocument;" or constructor with class names in variables // "new $controller;". if ($tokens[$constructor]['code'] === T_STRING - || $tokens[$constructor]['code'] === T_NS_SEPARATOR + || $tokens[$constructor]['code'] === T_NAME_QUALIFIED + || $tokens[$constructor]['code'] === T_NAME_FULLY_QUALIFIED + || $tokens[$constructor]['code'] === T_NAME_RELATIVE || ($tokens[$constructor]['code'] === T_VARIABLE && $tokens[($constructor + 1)]['code'] === T_SEMICOLON) ) { @@ -85,7 +87,9 @@ public function process(File $phpcsFile, $stackPtr) ); if ($nextConstructorPart === false || ($tokens[$nextConstructorPart]['code'] !== T_STRING - && $tokens[$nextConstructorPart]['code'] !== T_NS_SEPARATOR) + && $tokens[$nextConstructorPart]['code'] !== T_NAME_QUALIFIED + && $tokens[$nextConstructorPart]['code'] !== T_NAME_FULLY_QUALIFIED + && $tokens[$nextConstructorPart]['code'] !== T_NAME_RELATIVE) ) { break; } From d3691c1dff22beff84f909962f04c743d8f9c728 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 15:16:07 +0200 Subject: [PATCH 09/30] some namespace fixing --- .../Classes/FullyQualifiedNamespaceSniff.php | 33 +++++-------------- .../Classes/UnusedUseStatementSniff.php | 24 ++++---------- .../Sniffs/Classes/UseGlobalClassSniff.php | 16 ++++----- .../Classes/UseLeadingBackslashSniff.php | 4 +-- .../Commenting/DataTypeNamespaceSniff.php | 7 ++-- .../Commenting/VariableCommentSniff.php | 4 +-- 6 files changed, 29 insertions(+), 59 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php index eac0e35a..de195a08 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php @@ -11,6 +11,7 @@ use PHP_CodeSniffer\Files\File; use PHP_CodeSniffer\Sniffs\Sniff; +use PHP_CodeSniffer\Util\Tokens; /** * Checks that class references do not use FQN but use statements. @@ -30,7 +31,7 @@ class FullyQualifiedNamespaceSniff implements Sniff */ public function register() { - return [T_NS_SEPARATOR]; + return [T_NAME_FULLY_QUALIFIED]; }//end register() @@ -61,7 +62,9 @@ public function process(File $phpcsFile, $stackPtr) // We are only interested in a backslash embedded between strings, which // means this is a class reference with more than one namespace part. - if ($tokens[($stackPtr - 1)]['code'] !== T_STRING || $tokens[($stackPtr + 1)]['code'] !== T_STRING) { + if (substr_count($tokens[$stackPtr]['content'], '\\') === 1 + && strpos($tokens[$stackPtr]['content'], '\\') === 0 + ) { return; } @@ -78,18 +81,7 @@ public function process(File $phpcsFile, $stackPtr) $before = $phpcsFile->findPrevious([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], $stackPtr, null, true); } - // If this is a namespaced function call then ignore this because use - // statements for functions are not possible in PHP 5.5 and lower. - $after = $phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], $stackPtr, null, true); - if ($tokens[$after]['code'] === T_OPEN_PARENTHESIS - && $tokens[$before]['code'] !== T_NEW - && $tokens[$before]['code'] !== T_ATTRIBUTE - ) { - return ($after + 1); - } - - $fullName = $phpcsFile->getTokensAsString(($before + 1), ($after - 1 - $before)); - $fullName = trim($fullName, "\ \n"); + $fullName = trim($tokens[$stackPtr]['content']); $parts = explode('\\', $fullName); $className = end($parts); @@ -101,7 +93,7 @@ public function process(File $phpcsFile, $stackPtr) $useStatement = $phpcsFile->findNext(T_USE, 0); while ($useStatement !== false && empty($tokens[$useStatement]['conditions']) === true) { $endPtr = $phpcsFile->findEndOfStatement($useStatement); - $useEnd = ($phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], ($useStatement + 1), null, true) - 1); + $useEnd = ($phpcsFile->findNext(Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS, ($useStatement + 1), null, true) - 1); $useFullName = trim($phpcsFile->getTokensAsString(($useStatement + 1), ($useEnd - $useStatement))); // Check if use statement contains an alias. @@ -163,18 +155,11 @@ public function process(File $phpcsFile, $stackPtr) if ($fix === true) { $phpcsFile->fixer->beginChangeset(); - // Replace the fully qualified name with the local name. - for ($i = ($before + 1); $i < $after; $i++) { - if ($tokens[$i]['code'] !== T_WHITESPACE) { - $phpcsFile->fixer->replaceToken($i, ''); - } - } - // Use alias name if available. if ($aliasName !== false) { - $phpcsFile->fixer->addContentBefore(($after - 1), $aliasName); + $phpcsFile->fixer->replaceToken($stackPtr, $aliasName); } else { - $phpcsFile->fixer->addContentBefore(($after - 1), $className); + $phpcsFile->fixer->replaceToken($stackPtr, $className); } // Insert use statement at the beginning of the file if it is not there diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php index 8f98d4fb..aca42d5d 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php @@ -67,7 +67,7 @@ public function process(File $phpcsFile, $stackPtr) true ); - if ($tokens[$classPtr]['code'] !== T_STRING) { + if ($tokens[$classPtr]['code'] !== T_NAME_QUALIFIED) { return; } @@ -75,7 +75,8 @@ public function process(File $phpcsFile, $stackPtr) // insensitive, that's why we cannot search for the exact class name string // and need to iterate over all T_STRING tokens in the file. $classUsed = $phpcsFile->findNext(T_STRING, ($classPtr + 1)); - $lowerClassName = strtolower($tokens[$classPtr]['content']); + $classParts = explode('\\', $tokens[$classPtr]['content']); + $lowerClassName = strtolower(array_pop($classParts)); // Check if the referenced class is in the same namespace as the current // file. If it is then the use statement is not necessary. @@ -86,23 +87,16 @@ public function process(File $phpcsFile, $stackPtr) if ($namespacePtr !== false && $aliasUsed === false) { $nsEnd = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - T_WHITESPACE, - ], + Tokens::NAME_TOKENS + Tokens::EMPTY_TOKENS, ($namespacePtr + 1), null, true ); $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); - $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); + $useNamespacePtr = $phpcsFile->findNext(Tokens::NAME_TOKENS, ($stackPtr + 1)); $useNamespaceEnd = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - ], + Tokens::NAME_TOKENS, ($useNamespacePtr + 1), null, true @@ -128,7 +122,6 @@ public function process(File $phpcsFile, $stackPtr) $tokens[$beforeUsage]['code'], [ T_USE, - T_NS_SEPARATOR, // If an object operator is used then this is a method call // with the same name as the class name. Which means this is // not referring to the class. @@ -175,10 +168,7 @@ public function process(File $phpcsFile, $stackPtr) // name. $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); $useNamespaceEnd = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - ], + Tokens::NAME_TOKENS, ($useNamespacePtr + 1), null, true diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php index 7319f104..9197cdc3 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php @@ -75,19 +75,19 @@ public function process(File $phpcsFile, $stackPtr) $lineStart = $stackPtr; // Iterate through a potential multiline use statement. while (false !== $lineEnd = $phpcsFile->findNext([T_SEMICOLON, T_COMMA], ($lineStart + 1), ($stmtEnd + 1))) { - // We are only interested in imports that contain no backslash, - // which means this is a class without a namespace. - // Also skip function imports. - if ($phpcsFile->findNext(T_NS_SEPARATOR, $lineStart, $lineEnd) !== false - || $phpcsFile->findNext(T_STRING, $lineStart, $lineEnd, false, 'function') !== false - ) { + // Skip function imports. + if ($phpcsFile->findNext(T_STRING, $lineStart, $lineEnd, false, 'function') !== false) { $lineStart = $lineEnd; continue; } - // The first string token is the class name. - $class = $phpcsFile->findNext(T_STRING, $lineStart, $lineEnd); + $class = $phpcsFile->findNext(Tokens::NAME_TOKENS, $lineStart, $lineEnd); $className = $tokens[$class]['content']; + if (strpos($className, '\\') !== false) { + // This is a namespaced class, skip it. + $lineStart = $lineEnd; + continue; + } // If there is more than one string token, the last one is the alias. $alias = $phpcsFile->findPrevious(T_STRING, $lineEnd, $stackPtr); $aliasName = $tokens[$alias]['content']; diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UseLeadingBackslashSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UseLeadingBackslashSniff.php index 200ae848..0c323f6d 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UseLeadingBackslashSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UseLeadingBackslashSniff.php @@ -61,11 +61,11 @@ public function process(File $phpcsFile, $stackPtr) true ); - if ($startPtr !== false && $tokens[$startPtr]['code'] === T_NS_SEPARATOR) { + if ($startPtr !== false && $tokens[$startPtr]['code'] === T_NAME_FULLY_QUALIFIED) { $error = 'When importing a class with "use", do not include a leading \\'; $fix = $phpcsFile->addFixableError($error, $startPtr, 'SeparatorStart'); if ($fix === true) { - $phpcsFile->fixer->replaceToken($startPtr, ''); + $phpcsFile->fixer->replaceToken($startPtr, ltrim($tokens[$startPtr]['content'], '\\')); } } diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php index bed4c6d1..d3837c96 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php @@ -73,12 +73,9 @@ public function process(File $phpcsFile, $stackPtr) // Replace @var data types in doc comments with the fully qualified class // name. - $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); + $useNamespacePtr = $phpcsFile->findNext(Tokens::NAME_TOKENS, ($stackPtr + 1)); $useNamespaceEnd = $phpcsFile->findNext( - [ - T_NS_SEPARATOR, - T_STRING, - ], + Tokens::NAME_TOKENS, ($useNamespacePtr + 1), null, true diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php index aef359ee..0d3ab8af 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php @@ -47,8 +47,6 @@ public function processMemberVar(File $phpcsFile, $stackPtr) T_STATIC => T_STATIC, T_READONLY => T_READONLY, T_WHITESPACE => T_WHITESPACE, - T_STRING => T_STRING, - T_NS_SEPARATOR => T_NS_SEPARATOR, T_NAMESPACE => T_NAMESPACE, T_NULLABLE => T_NULLABLE, T_TYPE_UNION => T_TYPE_UNION, @@ -58,7 +56,7 @@ public function processMemberVar(File $phpcsFile, $stackPtr) T_FALSE => T_FALSE, T_SELF => T_SELF, T_PARENT => T_PARENT, - ] + Tokens::PHPCS_ANNOTATION_TOKENS); + ] + Tokens::PHPCS_ANNOTATION_TOKENS + Tokens::NAME_TOKENS); for ($commentEnd = ($stackPtr - 1); $commentEnd >= 0; $commentEnd--) { if (isset($ignore[$tokens[$commentEnd]['code']]) === true) { From 69fe1dd8b4cfe3eab7d7873f39b4635a49f8da06 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 15:45:05 +0200 Subject: [PATCH 10/30] file name fix --- coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php | 4 +--- tests/Drupal/Classes/ClassFileNameUnitTest.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php index 9197cdc3..4d0c2ebb 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php @@ -125,9 +125,7 @@ public function process(File $phpcsFile, $stackPtr) // Only start looking after the end of the use statement block. $i = $bodyStart; while (false !== $i = $phpcsFile->findNext(T_STRING, ($i + 1), null, false, $aliasName)) { - if ($tokens[($i - 1)]['code'] !== T_NS_SEPARATOR) { - $phpcsFile->fixer->replaceToken($i, '\\'.$className); - } + $phpcsFile->fixer->replaceToken($i, '\\'.$className); } $phpcsFile->fixer->endChangeset(); diff --git a/tests/Drupal/Classes/ClassFileNameUnitTest.php b/tests/Drupal/Classes/ClassFileNameUnitTest.php index 7cc4349a..c1fd3269 100644 --- a/tests/Drupal/Classes/ClassFileNameUnitTest.php +++ b/tests/Drupal/Classes/ClassFileNameUnitTest.php @@ -59,7 +59,7 @@ protected function getTestFiles($testFileBase): array return [ __DIR__.'/drupal8/ClassFileNameUnitTest.php', __DIR__.'/drupal8/drupal8.behat.inc', - __DIR__.'/drupal7/class_fle_name_test.module', + __DIR__.'/drupal7/class_file_name_test.module', __DIR__.'/drupal8/markdownFile.md', ]; From 64a80fc3fecea23c40b1a7b20f5c718a05a3327e Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 15:58:21 +0200 Subject: [PATCH 11/30] fix part 1 unused use statements --- coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php index aca42d5d..7080b499 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php @@ -67,7 +67,7 @@ public function process(File $phpcsFile, $stackPtr) true ); - if ($tokens[$classPtr]['code'] !== T_NAME_QUALIFIED) { + if (in_array($tokens[$classPtr]['code'], Tokens::NAME_TOKENS) === false) { return; } From fa946e7c5177c6d221fa91c9aecfd0e5241361bb Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 16:28:42 +0200 Subject: [PATCH 12/30] fix unused use test --- .../Drupal/Sniffs/Classes/UnusedUseStatementSniff.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php index 7080b499..34ab7342 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php @@ -93,15 +93,7 @@ public function process(File $phpcsFile, $stackPtr) true ); $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); - - $useNamespacePtr = $phpcsFile->findNext(Tokens::NAME_TOKENS, ($stackPtr + 1)); - $useNamespaceEnd = $phpcsFile->findNext( - Tokens::NAME_TOKENS, - ($useNamespacePtr + 1), - null, - true - ); - $useNamespace = rtrim($phpcsFile->getTokensAsString($useNamespacePtr, ($useNamespaceEnd - $useNamespacePtr - 1)), '\\'); + $useNamespace = implode('\\', $classParts); if (strcasecmp($namespace, $useNamespace) === 0) { $classUsed = false; From c28645c4a659076b4b434bdccceb7f1199778bfe Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Fri, 17 Oct 2025 16:47:23 +0200 Subject: [PATCH 13/30] data type fixing --- .../Sniffs/Commenting/DataTypeNamespaceSniff.php | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php index d3837c96..0e6bd11b 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php @@ -67,20 +67,14 @@ public function process(File $phpcsFile, $stackPtr) true ); - if ($tokens[$classPtr]['code'] !== T_STRING) { + if (in_array($tokens[$classPtr]['code'], Tokens::NAME_TOKENS) === false) { return; } // Replace @var data types in doc comments with the fully qualified class // name. - $useNamespacePtr = $phpcsFile->findNext(Tokens::NAME_TOKENS, ($stackPtr + 1)); - $useNamespaceEnd = $phpcsFile->findNext( - Tokens::NAME_TOKENS, - ($useNamespacePtr + 1), - null, - true - ); - $fullNamespace = $phpcsFile->getTokensAsString($useNamespacePtr, ($useNamespaceEnd - $useNamespacePtr)); + $fullNamespace = $tokens[$classPtr]['content']; + $className = substr($fullNamespace, strrpos($fullNamespace, '\\') + 1); $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($stackPtr + 1)); @@ -93,13 +87,13 @@ public function process(File $phpcsFile, $stackPtr) && $tokens[($tag + 1)]['code'] === T_DOC_COMMENT_WHITESPACE && isset($tokens[($tag + 2)]) === true && $tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING - && strpos($tokens[($tag + 2)]['content'], $tokens[$classPtr]['content']) === 0 + && strpos($tokens[($tag + 2)]['content'], $className) === 0 ) { $error = 'Data types in %s tags need to be fully namespaced'; $data = [$tokens[$tag]['content']]; $fix = $phpcsFile->addFixableError($error, ($tag + 2), 'DataTypeNamespace', $data); if ($fix === true) { - $replacement = '\\'.$fullNamespace.substr($tokens[($tag + 2)]['content'], strlen($tokens[$classPtr]['content'])); + $replacement = '\\'.$fullNamespace.substr($tokens[($tag + 2)]['content'], strlen($className)); $phpcsFile->fixer->replaceToken(($tag + 2), $replacement); } } From 0e38b85c390b11af8eb577a0f89ce5d821bc801c Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 15:18:31 +0200 Subject: [PATCH 14/30] fix good tests --- coder_sniffer/Drupal/ruleset.xml | 4 ++++ tests/Drupal/good/good.php | 11 ----------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/coder_sniffer/Drupal/ruleset.xml b/coder_sniffer/Drupal/ruleset.xml index 5eb990f3..4a9f4afe 100644 --- a/coder_sniffer/Drupal/ruleset.xml +++ b/coder_sniffer/Drupal/ruleset.xml @@ -37,6 +37,10 @@ 0 + + + *.tpl.php + diff --git a/tests/Drupal/good/good.php b/tests/Drupal/good/good.php index f9747220..7c8aa699 100644 --- a/tests/Drupal/good/good.php +++ b/tests/Drupal/good/good.php @@ -1397,17 +1397,6 @@ public function test() { } -// Namespaced function call is allowed because PHP 5.5 and lower do not support -// use statements for functions. -$default_config = [ - 'verify' => TRUE, - 'timeout' => 30, - 'headers' => [ - 'User-Agent' => 'Drupal/' . \Drupal::VERSION . ' (+https://www.drupal.org/) ' . \GuzzleHttp\default_user_agent(), - ], - 'handler' => $stack, -]; - // camelCase and snake_case variables are allowed. $snake_case = 1; $camelCase = 1; From efb89c550c0adaaf3517ef80a02d6f6b6b54e84d Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 15:35:02 +0200 Subject: [PATCH 15/30] partial use statement fix --- .../Sniffs/Classes/FullyQualifiedNamespaceSniff.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php index de195a08..6fe07ec8 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php @@ -31,7 +31,7 @@ class FullyQualifiedNamespaceSniff implements Sniff */ public function register() { - return [T_NAME_FULLY_QUALIFIED]; + return [T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED]; }//end register() @@ -74,11 +74,9 @@ public function process(File $phpcsFile, $stackPtr) } // Check if this is a use statement and ignore those. - $before = $phpcsFile->findPrevious([T_STRING, T_NS_SEPARATOR, T_WHITESPACE, T_COMMA, T_AS], $stackPtr, null, true); + $before = $phpcsFile->findPrevious(Tokens::EMPTY_TOKENS, $stackPtr - 1, null, true); if ($tokens[$before]['code'] === T_USE || $tokens[$before]['code'] === T_NAMESPACE) { - return $phpcsFile->findNext([T_STRING, T_NS_SEPARATOR, T_WHITESPACE, T_COMMA, T_AS], ($stackPtr + 1), null, true); - } else { - $before = $phpcsFile->findPrevious([T_STRING, T_NS_SEPARATOR, T_WHITESPACE], $stackPtr, null, true); + return; } $fullName = trim($tokens[$stackPtr]['content']); From 84c9ad62d00afdece9d5cd699d9ea4367320db80 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 15:56:03 +0200 Subject: [PATCH 16/30] fix muktiline use statements --- .../Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php index 6fe07ec8..e7b84e79 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php @@ -74,7 +74,7 @@ public function process(File $phpcsFile, $stackPtr) } // Check if this is a use statement and ignore those. - $before = $phpcsFile->findPrevious(Tokens::EMPTY_TOKENS, $stackPtr - 1, null, true); + $before = $phpcsFile->findPrevious(Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS + [T_COMMA => T_COMMA, T_AS => T_AS], $stackPtr - 1, null, true); if ($tokens[$before]['code'] === T_USE || $tokens[$before]['code'] === T_NAMESPACE) { return; } From 81e63604bfe5817737a6307bdbb7b6152e685cf0 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 16:12:16 +0200 Subject: [PATCH 17/30] refactor to use SlevomatCodingStandard.ControlStructures.NewWithParentheses --- .../Classes/ClassCreateInstanceSniff.php | 129 ------------------ coder_sniffer/Drupal/ruleset.xml | 1 + .../Classes/ClassCreateInstanceUnitTest.php | 59 -------- tests/Drupal/bad/BadUnitTest.php | 16 +++ .../ClassCreateInstanceUnitTest.inc | 0 .../ClassCreateInstanceUnitTest.inc.fixed | 0 6 files changed, 17 insertions(+), 188 deletions(-) delete mode 100644 coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php delete mode 100644 tests/Drupal/Classes/ClassCreateInstanceUnitTest.php rename tests/Drupal/{Classes => bad}/ClassCreateInstanceUnitTest.inc (100%) rename tests/Drupal/{Classes => bad}/ClassCreateInstanceUnitTest.inc.fixed (100%) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php deleted file mode 100644 index cb12221a..00000000 --- a/coder_sniffer/Drupal/Sniffs/Classes/ClassCreateInstanceSniff.php +++ /dev/null @@ -1,129 +0,0 @@ - - */ - public function register() - { - return [T_NEW]; - - }//end register() - - - /** - * Processes this test, when one of its tokens is encountered. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token in the - * stack passed in $tokens. - * - * @return void - */ - public function process(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - $commaOrColon = $phpcsFile->findNext([T_SEMICOLON, T_COLON, T_COMMA], ($stackPtr + 1)); - if ($commaOrColon === false) { - // Syntax error, nothing we can do. - return; - } - - // Search for an opening parenthesis in the current statement until the - // next semicolon or comma. - $nextParenthesis = $phpcsFile->findNext(T_OPEN_PARENTHESIS, ($stackPtr + 1), $commaOrColon); - if ($nextParenthesis === false) { - $error = 'Calling class constructors must always include parentheses'; - $constructor = $phpcsFile->findNext(Tokens::EMPTY_TOKENS, ($stackPtr + 1), null, true, null, true); - // We can invoke the fixer if we know this is a static constructor - // function call or constructor calls with namespaces, example - // "new \DOMDocument;" or constructor with class names in variables - // "new $controller;". - if ($tokens[$constructor]['code'] === T_STRING - || $tokens[$constructor]['code'] === T_NAME_QUALIFIED - || $tokens[$constructor]['code'] === T_NAME_FULLY_QUALIFIED - || $tokens[$constructor]['code'] === T_NAME_RELATIVE - || ($tokens[$constructor]['code'] === T_VARIABLE - && $tokens[($constructor + 1)]['code'] === T_SEMICOLON) - ) { - // Scan to the end of possible string\namespace parts. - $nextConstructorPart = $constructor; - while (true) { - $nextConstructorPart = $phpcsFile->findNext( - Tokens::EMPTY_TOKENS, - ($nextConstructorPart + 1), - null, - true, - null, - true - ); - if ($nextConstructorPart === false - || ($tokens[$nextConstructorPart]['code'] !== T_STRING - && $tokens[$nextConstructorPart]['code'] !== T_NAME_QUALIFIED - && $tokens[$nextConstructorPart]['code'] !== T_NAME_FULLY_QUALIFIED - && $tokens[$nextConstructorPart]['code'] !== T_NAME_RELATIVE) - ) { - break; - } - - $constructor = $nextConstructorPart; - } - - $fix = $phpcsFile->addFixableError($error, $constructor, 'ParenthesisMissing'); - if ($fix === true) { - $phpcsFile->fixer->addContent($constructor, '()'); - } - - // We can invoke the fixer if we know this is a - // constructor call with class names in an array - // example "new $controller[$i];". - } else if ($tokens[$constructor]['code'] === T_VARIABLE - && $tokens[($constructor + 1)]['code'] === T_OPEN_SQUARE_BRACKET - ) { - // Scan to the end of possible multilevel arrays. - $nextConstructorPart = $constructor; - do { - $nextConstructorPart = $tokens[($nextConstructorPart + 1)]['bracket_closer']; - } while ($tokens[($nextConstructorPart + 1)]['code'] === T_OPEN_SQUARE_BRACKET); - - $fix = $phpcsFile->addFixableError($error, $nextConstructorPart, 'ParenthesisMissing'); - if ($fix === true) { - $phpcsFile->fixer->addContent($nextConstructorPart, '()'); - } - } else { - $phpcsFile->addError($error, $stackPtr, 'ParenthesisMissing'); - }//end if - }//end if - - }//end process() - - -}//end class diff --git a/coder_sniffer/Drupal/ruleset.xml b/coder_sniffer/Drupal/ruleset.xml index 4a9f4afe..d25745ec 100644 --- a/coder_sniffer/Drupal/ruleset.xml +++ b/coder_sniffer/Drupal/ruleset.xml @@ -140,6 +140,7 @@ + diff --git a/tests/Drupal/Classes/ClassCreateInstanceUnitTest.php b/tests/Drupal/Classes/ClassCreateInstanceUnitTest.php deleted file mode 100644 index e220ea96..00000000 --- a/tests/Drupal/Classes/ClassCreateInstanceUnitTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - */ - protected function getErrorList(string $testFile): array - { - return [ - 3 => 1, - 4 => 1, - 5 => 1, - 6 => 1, - 8 => 1, - 9 => 1, - 10 => 1, - 11 => 1, - 12 => 1, - 13 => 1, - 14 => 1, - 16 => 1, - 31 => 1, - ]; - - }//end getErrorList() - - - /** - * Returns the lines where warnings should occur. - * - * The key of the array should represent the line number and the value - * should represent the number of warnings that should occur on that line. - * - * @param string $testFile The name of the file being tested. - * - * @return array - */ - protected function getWarningList(string $testFile): array - { - return []; - - }//end getWarningList() - - -}//end class diff --git a/tests/Drupal/bad/BadUnitTest.php b/tests/Drupal/bad/BadUnitTest.php index 4312e07b..91111fa7 100644 --- a/tests/Drupal/bad/BadUnitTest.php +++ b/tests/Drupal/bad/BadUnitTest.php @@ -367,6 +367,22 @@ protected function getErrorList(string $testFile): array 872 => 1, 876 => 2, ]; + case 'ClassCreateInstanceUnitTest.inc': + return [ + 3 => 1, + 4 => 1, + 5 => 1, + 6 => 1, + 8 => 1, + 9 => 1, + 10 => 1, + 11 => 1, + 12 => 2, + 13 => 2, + 14 => 2, + 16 => 1, + 31 => 1, + ]; }//end switch return []; diff --git a/tests/Drupal/Classes/ClassCreateInstanceUnitTest.inc b/tests/Drupal/bad/ClassCreateInstanceUnitTest.inc similarity index 100% rename from tests/Drupal/Classes/ClassCreateInstanceUnitTest.inc rename to tests/Drupal/bad/ClassCreateInstanceUnitTest.inc diff --git a/tests/Drupal/Classes/ClassCreateInstanceUnitTest.inc.fixed b/tests/Drupal/bad/ClassCreateInstanceUnitTest.inc.fixed similarity index 100% rename from tests/Drupal/Classes/ClassCreateInstanceUnitTest.inc.fixed rename to tests/Drupal/bad/ClassCreateInstanceUnitTest.inc.fixed From 7d9cbafbca36945efee3e74886b7d969cdc6870f Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 16:30:48 +0200 Subject: [PATCH 18/30] refactor to SlevomatCodingStandard.Namespaces.UnusedUses --- .../Classes/UnusedUseStatementSniff.php | 193 ------------------ coder_sniffer/Drupal/ruleset.xml | 1 + .../Classes/UnusedUseStatementUnitTest.php | 60 ------ tests/Drupal/bad/BadUnitTest.php | 20 ++ .../UnusedUseStatementUnitTest.inc | 0 .../UnusedUseStatementUnitTest.inc.fixed | 0 6 files changed, 21 insertions(+), 253 deletions(-) delete mode 100644 coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php delete mode 100644 tests/Drupal/Classes/UnusedUseStatementUnitTest.php rename tests/Drupal/{Classes => bad}/UnusedUseStatementUnitTest.inc (100%) rename tests/Drupal/{Classes => bad}/UnusedUseStatementUnitTest.inc.fixed (100%) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php deleted file mode 100644 index 34ab7342..00000000 --- a/coder_sniffer/Drupal/Sniffs/Classes/UnusedUseStatementSniff.php +++ /dev/null @@ -1,193 +0,0 @@ - - */ - public function register() - { - return [T_USE]; - - }//end register() - - - /** - * Processes this test, when one of its tokens is encountered. - * - * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. - * @param int $stackPtr The position of the current token in - * the stack passed in $tokens. - * - * @return void - */ - public function process(File $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - // Only check use statements in the global scope. - if (empty($tokens[$stackPtr]['conditions']) === false) { - return; - } - - // Seek to the end of the statement and get the string before the semi colon. - $semiColon = $phpcsFile->findEndOfStatement($stackPtr); - if ($tokens[$semiColon]['code'] !== T_SEMICOLON) { - return; - } - - $classPtr = $phpcsFile->findPrevious( - Tokens::EMPTY_TOKENS, - ($semiColon - 1), - null, - true - ); - - if (in_array($tokens[$classPtr]['code'], Tokens::NAME_TOKENS) === false) { - return; - } - - // Search where the class name is used. PHP treats class names case - // insensitive, that's why we cannot search for the exact class name string - // and need to iterate over all T_STRING tokens in the file. - $classUsed = $phpcsFile->findNext(T_STRING, ($classPtr + 1)); - $classParts = explode('\\', $tokens[$classPtr]['content']); - $lowerClassName = strtolower(array_pop($classParts)); - - // Check if the referenced class is in the same namespace as the current - // file. If it is then the use statement is not necessary. - $namespacePtr = $phpcsFile->findPrevious([T_NAMESPACE], $stackPtr); - // Check if the use statement does aliasing with the "as" keyword. Aliasing - // is allowed even in the same namespace. - $aliasUsed = $phpcsFile->findPrevious(T_AS, ($classPtr - 1), $stackPtr); - - if ($namespacePtr !== false && $aliasUsed === false) { - $nsEnd = $phpcsFile->findNext( - Tokens::NAME_TOKENS + Tokens::EMPTY_TOKENS, - ($namespacePtr + 1), - null, - true - ); - $namespace = trim($phpcsFile->getTokensAsString(($namespacePtr + 1), ($nsEnd - $namespacePtr - 1))); - $useNamespace = implode('\\', $classParts); - - if (strcasecmp($namespace, $useNamespace) === 0) { - $classUsed = false; - } - }//end if - - while ($classUsed !== false) { - if (strtolower($tokens[$classUsed]['content']) === $lowerClassName) { - $beforeUsage = $phpcsFile->findPrevious( - Tokens::EMPTY_TOKENS, - ($classUsed - 1), - null, - true - ); - // If a backslash is used before the class name then this is some other - // use statement. - if (in_array( - $tokens[$beforeUsage]['code'], - [ - T_USE, - // If an object operator is used then this is a method call - // with the same name as the class name. Which means this is - // not referring to the class. - T_OBJECT_OPERATOR, - // Function definition, not class invocation. - T_FUNCTION, - // Static method call, not class invocation. - T_DOUBLE_COLON, - ] - ) === false - ) { - return; - } - - // Trait use statement within a class. - if ($tokens[$beforeUsage]['code'] === T_USE && empty($tokens[$beforeUsage]['conditions']) === false) { - return; - } - }//end if - - $classUsed = $phpcsFile->findNext([T_STRING], ($classUsed + 1)); - }//end while - - $warning = 'Unused use statement'; - $fix = $phpcsFile->addFixableWarning($warning, $stackPtr, 'UnusedUse'); - if ($fix === true) { - // Remove the whole use statement line. - $phpcsFile->fixer->beginChangeset(); - for ($i = $stackPtr; $i <= $semiColon; $i++) { - $phpcsFile->fixer->replaceToken($i, ''); - } - - // Also remove whitespace after the semicolon (new lines). - while (isset($tokens[$i]) === true && $tokens[$i]['code'] === T_WHITESPACE) { - $phpcsFile->fixer->replaceToken($i, ''); - if (strpos($tokens[$i]['content'], $phpcsFile->eolChar) !== false) { - break; - } - - $i++; - } - - // Replace @var data types in doc comments with the fully qualified class - // name. - $useNamespacePtr = $phpcsFile->findNext([T_STRING], ($stackPtr + 1)); - $useNamespaceEnd = $phpcsFile->findNext( - Tokens::NAME_TOKENS, - ($useNamespacePtr + 1), - null, - true - ); - $fullNamespace = $phpcsFile->getTokensAsString($useNamespacePtr, ($useNamespaceEnd - $useNamespacePtr)); - - $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($stackPtr + 1)); - - while ($tag !== false) { - if (($tokens[$tag]['content'] === '@var' || $tokens[$tag]['content'] === '@return') - && isset($tokens[($tag + 1)]) === true - && $tokens[($tag + 1)]['code'] === T_DOC_COMMENT_WHITESPACE - && isset($tokens[($tag + 2)]) === true - && $tokens[($tag + 2)]['code'] === T_DOC_COMMENT_STRING - && strpos($tokens[($tag + 2)]['content'], $tokens[$classPtr]['content']) === 0 - ) { - $replacement = '\\'.$fullNamespace.substr($tokens[($tag + 2)]['content'], strlen($tokens[$classPtr]['content'])); - $phpcsFile->fixer->replaceToken(($tag + 2), $replacement); - } - - $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($tag + 1)); - } - - $phpcsFile->fixer->endChangeset(); - }//end if - - }//end process() - - -}//end class diff --git a/coder_sniffer/Drupal/ruleset.xml b/coder_sniffer/Drupal/ruleset.xml index d25745ec..e77d6ff2 100644 --- a/coder_sniffer/Drupal/ruleset.xml +++ b/coder_sniffer/Drupal/ruleset.xml @@ -142,6 +142,7 @@ + diff --git a/tests/Drupal/Classes/UnusedUseStatementUnitTest.php b/tests/Drupal/Classes/UnusedUseStatementUnitTest.php deleted file mode 100644 index 70950b9e..00000000 --- a/tests/Drupal/Classes/UnusedUseStatementUnitTest.php +++ /dev/null @@ -1,60 +0,0 @@ - - */ - protected function getErrorList(string $testFile): array - { - return []; - - }//end getErrorList() - - - /** - * Returns the lines where warnings should occur. - * - * The key of the array should represent the line number and the value - * should represent the number of warnings that should occur on that line. - * - * @param string $testFile The name of the file being tested. - * - * @return array - */ - protected function getWarningList(string $testFile): array - { - return [ - 5 => 1, - 6 => 1, - 7 => 1, - 10 => 1, - 11 => 1, - 12 => 1, - 14 => 1, - 16 => 1, - 17 => 1, - 19 => 1, - 20 => 1, - 21 => 1, - 22 => 1, - 23 => 1, - ]; - - }//end getWarningList() - - -}//end class diff --git a/tests/Drupal/bad/BadUnitTest.php b/tests/Drupal/bad/BadUnitTest.php index 91111fa7..cabd29c7 100644 --- a/tests/Drupal/bad/BadUnitTest.php +++ b/tests/Drupal/bad/BadUnitTest.php @@ -383,6 +383,26 @@ protected function getErrorList(string $testFile): array 16 => 1, 31 => 1, ]; + case 'UnusedUseStatementUnitTest.inc': + return [ + 5 => 1, + 6 => 1, + 7 => 1, + 10 => 1, + 11 => 1, + 12 => 1, + 14 => 1, + 17 => 1, + 19 => 1, + 20 => 1, + 21 => 1, + 22 => 1, + 23 => 1, + 35 => 1, + 56 => 1, + 85 => 1, + 98 => 1, + ]; }//end switch return []; diff --git a/tests/Drupal/Classes/UnusedUseStatementUnitTest.inc b/tests/Drupal/bad/UnusedUseStatementUnitTest.inc similarity index 100% rename from tests/Drupal/Classes/UnusedUseStatementUnitTest.inc rename to tests/Drupal/bad/UnusedUseStatementUnitTest.inc diff --git a/tests/Drupal/Classes/UnusedUseStatementUnitTest.inc.fixed b/tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed similarity index 100% rename from tests/Drupal/Classes/UnusedUseStatementUnitTest.inc.fixed rename to tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed From 6b7c9d0bf7894c19a7f44a321530602b3711aedf Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 16:56:58 +0200 Subject: [PATCH 19/30] refactor to use SlevomatCodingStandard.Namespaces.UseFromSameNamespace --- coder_sniffer/Drupal/ruleset.xml | 1 + tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc | 4 ++-- .../Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc.fixed | 4 ++-- tests/Drupal/Classes/UseGlobalClassUnitTest.inc.fixed | 1 - tests/Drupal/bad/BadUnitTest.php | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/coder_sniffer/Drupal/ruleset.xml b/coder_sniffer/Drupal/ruleset.xml index e77d6ff2..7c6ad8d4 100644 --- a/coder_sniffer/Drupal/ruleset.xml +++ b/coder_sniffer/Drupal/ruleset.xml @@ -143,6 +143,7 @@ + diff --git a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc index 1819ef46..5ddd6d05 100644 --- a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc +++ b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc @@ -8,8 +8,8 @@ use Test\Bar; use Test\NotUsed; use Test\Alias as TestAlias; -use Test\MultiLine as MultiLineAlias, - Test\MultiLineSecond; +use Test\MultiLine as MultiLineAlias; +use Test\MultiLineSecond; /** * Example. diff --git a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc.fixed b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc.fixed index 004f9eaa..277c94b8 100644 --- a/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc.fixed +++ b/tests/Drupal/Classes/FullyQualifiedNamespaceUnitTest.inc.fixed @@ -6,11 +6,11 @@ */ use Test\MultiLineSecond; +use Test\MultiLine; use Test\Foo; use Test\NotUsed; use Test\Bar; use Test\Alias as TestAlias; -use Test\MultiLine as MultiLineAlias; /** * Example. @@ -69,7 +69,7 @@ class Example { /** * Description. */ - public function test8(MultiLineAlias $multiLine, MultiLineSecond $multiLineSecond) { + public function test8(MultiLine $multiLine, MultiLineSecond $multiLineSecond) { } diff --git a/tests/Drupal/Classes/UseGlobalClassUnitTest.inc.fixed b/tests/Drupal/Classes/UseGlobalClassUnitTest.inc.fixed index a1518555..40081c19 100644 --- a/tests/Drupal/Classes/UseGlobalClassUnitTest.inc.fixed +++ b/tests/Drupal/Classes/UseGlobalClassUnitTest.inc.fixed @@ -8,7 +8,6 @@ use Namespaced\TestClass; use Namespaced\TestClassSecond as NamespacedAlias; use Namespaced\MultiLine2 as MultiLineAlias2; -use function count; /** * Example. diff --git a/tests/Drupal/bad/BadUnitTest.php b/tests/Drupal/bad/BadUnitTest.php index cabd29c7..c9a60d1c 100644 --- a/tests/Drupal/bad/BadUnitTest.php +++ b/tests/Drupal/bad/BadUnitTest.php @@ -392,7 +392,8 @@ protected function getErrorList(string $testFile): array 11 => 1, 12 => 1, 14 => 1, - 17 => 1, + 16 => 1, + 17 => 2, 19 => 1, 20 => 1, 21 => 1, From 8000cd5de20ffb42f7883f4c384f8b54967b452e Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 17:18:59 +0200 Subject: [PATCH 20/30] fix use statement comparison --- .../Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php index e7b84e79..b549face 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php @@ -79,7 +79,7 @@ public function process(File $phpcsFile, $stackPtr) return; } - $fullName = trim($tokens[$stackPtr]['content']); + $fullName = trim($tokens[$stackPtr]['content'], '\\ '); $parts = explode('\\', $fullName); $className = end($parts); @@ -199,10 +199,6 @@ public function process(File $phpcsFile, $stackPtr) $phpcsFile->fixer->endChangeset(); }//end if - // Continue after this class reference so that errors for this are not - // flagged multiple times. - return $phpcsFile->findNext([T_STRING, T_NS_SEPARATOR], ($stackPtr + 1), null, true); - }//end process() From af8c9c75ae3500c2a108885cb0cceb5bb1ce5390 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 17:24:25 +0200 Subject: [PATCH 21/30] ignore var fixes --- tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed b/tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed index 2c620518..93b5fe68 100644 --- a/tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed +++ b/tests/Drupal/bad/UnusedUseStatementUnitTest.inc.fixed @@ -26,7 +26,7 @@ class Pum { /** * Aliased type that is otherwise unused. * - * @var \Some\Data\VarName2 + * @var AliasVarName2 */ protected $y; @@ -71,7 +71,7 @@ class Pum { protected function test6($x) { /** @var \Some\Data\VarName $y */ $y = $x['test']; - /** @var \Some\Data\VarName2 $z */ + /** @var AliasVarName2 $z */ $z = $x['test2']; return $y; } From 51d2b9d79250b2ea79458d50f5bfef3a7fcd70db Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 17:53:49 +0200 Subject: [PATCH 22/30] fix crlf problem --- .../Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php | 4 ++++ tests/Drupal/bad/bad_crlf.inc.fixed | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php b/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php index 0ac20d1f..5c96ffae 100644 --- a/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php +++ b/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php @@ -48,6 +48,10 @@ public function register() */ public function process(File $phpcsFile, $stackPtr) { + // This sniff only works when there are \n line endings. + if ($phpcsFile->eolChar !== "\n") { + return $phpcsFile->numTokens + 1; + } $tokens = $phpcsFile->getTokens(); // Only check the very first PHP open tag in a file, ignore any others. diff --git a/tests/Drupal/bad/bad_crlf.inc.fixed b/tests/Drupal/bad/bad_crlf.inc.fixed index ad434228..1fd9757b 100644 --- a/tests/Drupal/bad/bad_crlf.inc.fixed +++ b/tests/Drupal/bad/bad_crlf.inc.fixed @@ -5,4 +5,6 @@ namespace Drupal\example\Controller; /** * Foo. */ -class ExampleController {} +class ExampleController { + +} From 25ea8f9ab2edc950f5963a478e0725d525981c5d Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 17:57:25 +0200 Subject: [PATCH 23/30] coding standards --- .../Sniffs/Classes/FullyQualifiedNamespaceSniff.php | 9 ++++++--- .../Drupal/Sniffs/Classes/UseGlobalClassSniff.php | 1 + .../Sniffs/Commenting/DataTypeNamespaceSniff.php | 2 +- .../Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php | 3 ++- coder_sniffer/DrupalPractice/Project.php | 5 +++-- .../DrupalPractice/Sniffs/Objects/GlobalClassSniff.php | 3 ++- phpcs.xml.dist | 5 +++-- tests/Drupal/CoderSniffUnitTest.php | 10 ++++++---- 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php index b549face..c4ef66e6 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/FullyQualifiedNamespaceSniff.php @@ -31,7 +31,10 @@ class FullyQualifiedNamespaceSniff implements Sniff */ public function register() { - return [T_NAME_FULLY_QUALIFIED, T_NAME_QUALIFIED]; + return [ + T_NAME_FULLY_QUALIFIED, + T_NAME_QUALIFIED, + ]; }//end register() @@ -74,7 +77,7 @@ public function process(File $phpcsFile, $stackPtr) } // Check if this is a use statement and ignore those. - $before = $phpcsFile->findPrevious(Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS + [T_COMMA => T_COMMA, T_AS => T_AS], $stackPtr - 1, null, true); + $before = $phpcsFile->findPrevious((Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS + [T_COMMA => T_COMMA, T_AS => T_AS]), ($stackPtr - 1), null, true); if ($tokens[$before]['code'] === T_USE || $tokens[$before]['code'] === T_NAMESPACE) { return; } @@ -91,7 +94,7 @@ public function process(File $phpcsFile, $stackPtr) $useStatement = $phpcsFile->findNext(T_USE, 0); while ($useStatement !== false && empty($tokens[$useStatement]['conditions']) === true) { $endPtr = $phpcsFile->findEndOfStatement($useStatement); - $useEnd = ($phpcsFile->findNext(Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS, ($useStatement + 1), null, true) - 1); + $useEnd = ($phpcsFile->findNext((Tokens::EMPTY_TOKENS + Tokens::NAME_TOKENS), ($useStatement + 1), null, true) - 1); $useFullName = trim($phpcsFile->getTokensAsString(($useStatement + 1), ($useEnd - $useStatement))); // Check if use statement contains an alias. diff --git a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php index 4d0c2ebb..2bf03c08 100644 --- a/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Classes/UseGlobalClassSniff.php @@ -88,6 +88,7 @@ public function process(File $phpcsFile, $stackPtr) $lineStart = $lineEnd; continue; } + // If there is more than one string token, the last one is the alias. $alias = $phpcsFile->findPrevious(T_STRING, $lineEnd, $stackPtr); $aliasName = $tokens[$alias]['content']; diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php index 0e6bd11b..7602dca7 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/DataTypeNamespaceSniff.php @@ -74,7 +74,7 @@ public function process(File $phpcsFile, $stackPtr) // Replace @var data types in doc comments with the fully qualified class // name. $fullNamespace = $tokens[$classPtr]['content']; - $className = substr($fullNamespace, strrpos($fullNamespace, '\\') + 1); + $className = substr($fullNamespace, (strrpos($fullNamespace, '\\') + 1)); $tag = $phpcsFile->findNext(T_DOC_COMMENT_TAG, ($stackPtr + 1)); diff --git a/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php b/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php index 5c96ffae..302b52ac 100644 --- a/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php +++ b/coder_sniffer/Drupal/Sniffs/WhiteSpace/OpenTagNewlineSniff.php @@ -50,8 +50,9 @@ public function process(File $phpcsFile, $stackPtr) { // This sniff only works when there are \n line endings. if ($phpcsFile->eolChar !== "\n") { - return $phpcsFile->numTokens + 1; + return ($phpcsFile->numTokens + 1); } + $tokens = $phpcsFile->getTokens(); // Only check the very first PHP open tag in a file, ignore any others. diff --git a/coder_sniffer/DrupalPractice/Project.php b/coder_sniffer/DrupalPractice/Project.php index d8322e47..464e6166 100644 --- a/coder_sniffer/DrupalPractice/Project.php +++ b/coder_sniffer/DrupalPractice/Project.php @@ -195,9 +195,10 @@ public static function isServiceClass(File $phpcsFile, $classPtr) $cache[$phpcsFile->getFilename()] = false; return false; } + $nameQualifiedPtr = $phpcsFile->findNext(T_NAME_QUALIFIED, ($namespacePtr + 1)); - $namespace = $phpcsFile->getTokens()[$nameQualifiedPtr]['content'] ?? ''; - $classNameSpaced = ltrim($namespace.'\\'.$phpcsFile->getDeclarationName($classPtr), '\\'); + $namespace = ($phpcsFile->getTokens()[$nameQualifiedPtr]['content'] ?? ''); + $classNameSpaced = ltrim($namespace.'\\'.$phpcsFile->getDeclarationName($classPtr), '\\'); foreach ($services['services'] as $service) { if (isset($service['class']) === true diff --git a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php index b94011e8..e19ca290 100644 --- a/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php +++ b/coder_sniffer/DrupalPractice/Sniffs/Objects/GlobalClassSniff.php @@ -181,13 +181,14 @@ protected function getFullyQualifiedName(File $phpcsFile, $className) { $useStatement = $phpcsFile->findNext(T_USE, 0); while ($useStatement !== false) { - $endPtr = $phpcsFile->findEndOfStatement($useStatement); + $endPtr = $phpcsFile->findEndOfStatement($useStatement); $useFullNamePtr = $phpcsFile->findNext([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED], ($useStatement + 1), $endPtr); if ($useFullNamePtr === false) { // No qualified name found, skip to next use statement. $useStatement = $phpcsFile->findNext(T_USE, ($endPtr + 1)); continue; } + $useFullName = trim($phpcsFile->getTokens()[$useFullNamePtr]['content'], '\\ '); // Check if use statement contains an alias. diff --git a/phpcs.xml.dist b/phpcs.xml.dist index f48d02d1..650e6c16 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -106,7 +106,6 @@ - @@ -154,7 +153,9 @@ - + + + diff --git a/tests/Drupal/CoderSniffUnitTest.php b/tests/Drupal/CoderSniffUnitTest.php index a2c7851e..500888a8 100644 --- a/tests/Drupal/CoderSniffUnitTest.php +++ b/tests/Drupal/CoderSniffUnitTest.php @@ -17,7 +17,6 @@ use PHP_CodeSniffer\Ruleset; use PHP_CodeSniffer\Files\LocalFile; use PHP_CodeSniffer\Exceptions\RuntimeException; -use PHP_CodeSniffer\Util\Common; use PHP_CodeSniffer\Util\Tokens; use PHPUnit\Framework\TestCase; @@ -420,6 +419,7 @@ public function generateFailureMessages(LocalFile $file): array }//end generateFailureMessages() + /** * Given a test class name, returns the code for the sniff. * @@ -448,14 +448,16 @@ public function getSniffCode(string $testClass): string $sniff = substr($sniff, 0, -8); } else { throw new \InvalidArgumentException( - 'The $testClass parameter was not passed a fully qualified sniff(test) class name. Received: ' . $testClass + 'The $testClass parameter was not passed a fully qualified sniff(test) class name. Received: '.$testClass ); } $standard = $parts[($partsCount - 4)]; $category = $parts[($partsCount - 2)]; - return $standard . '.' . $category . '.' . $sniff; - } + return $standard.'.'.$category.'.'.$sniff; + + }//end getSniffCode() + /** * Set a list of CLI values before the file is tested. From a881e6da6dc3606a2c22ea9e131f687d8004da1a Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 18:03:08 +0200 Subject: [PATCH 24/30] fix phpstan --- .../Drupal/Sniffs/Commenting/FileCommentSniff.php | 8 +------- tests/Drupal/CoderSniffUnitTest.php | 2 +- tests/Drupal/bad/BadUnitTest.php | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php index 78b6abae..fc1d8de3 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/FileCommentSniff.php @@ -162,13 +162,7 @@ public function process(File $phpcsFile, $stackPtr) if ($fileTag === false) { $fix = $phpcsFile->addFixableError('Missing file doc comment', $stackPtr, 'Missing'); if ($fix === true) { - // Only PHP has a real opening tag, additional newline at the - // beginning here. - if ($phpcsFile->tokenizerType === 'PHP') { - $phpcsFile->fixer->addContent($stackPtr, "\n/**\n * @file\n */\n"); - } else { - $phpcsFile->fixer->addContent($stackPtr, "/**\n * @file\n */\n"); - } + $phpcsFile->fixer->addContent($stackPtr, "\n/**\n * @file\n */\n"); } return ($phpcsFile->numTokens + 1); diff --git a/tests/Drupal/CoderSniffUnitTest.php b/tests/Drupal/CoderSniffUnitTest.php index 500888a8..e4fb08fb 100644 --- a/tests/Drupal/CoderSniffUnitTest.php +++ b/tests/Drupal/CoderSniffUnitTest.php @@ -434,7 +434,7 @@ public function generateFailureMessages(LocalFile $file): array */ public function getSniffCode(string $testClass): string { - if (is_string($testClass) === false || $testClass === '') { + if ($testClass === '') { throw new \InvalidArgumentException('The $testClass parameter must be a non-empty string'); } diff --git a/tests/Drupal/bad/BadUnitTest.php b/tests/Drupal/bad/BadUnitTest.php index c9a60d1c..bd0ebf86 100644 --- a/tests/Drupal/bad/BadUnitTest.php +++ b/tests/Drupal/bad/BadUnitTest.php @@ -29,7 +29,7 @@ protected function getErrorList(string $testFile): array switch ($testFile) { case 'bad_crlf.inc': return [ - 1 => 2, + 1 => 1, 8 => 1, ]; case 'bad.info': From 94d070eaabb6992f2ac567a8b398448c3a93e1cb Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sat, 18 Oct 2025 18:08:54 +0200 Subject: [PATCH 25/30] bump to PHP 7.4 minimum --- .github/workflows/testing.yml | 2 +- composer.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index f3183d9b..f5cdaa38 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -7,7 +7,7 @@ jobs: strategy: fail-fast: false matrix: - php-versions: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3'] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3'] extra-tests: ['0'] # We only need to run PHPStan and Drupal core regression tests once on # the latest PHP version. diff --git a/composer.json b/composer.json index 18661bef..c9e0ec26 100644 --- a/composer.json +++ b/composer.json @@ -14,7 +14,7 @@ ], "license": "GPL-2.0-or-later", "require": { - "php": ">=7.2", + "php": ">=7.4", "ext-mbstring": "*", "dealerdirect/phpcodesniffer-composer-installer": "^0.7.1 || ^1.0.0", "sirbrillig/phpcs-variable-analysis": "^2.13", @@ -36,6 +36,6 @@ }, "require-dev": { "phpstan/phpstan": "^1.7.12", - "phpunit/phpunit": "^8.0" + "phpunit/phpunit": "^9.0" } } From b4df5a4e4bc4ba516ab4589e2b44d93881e5d1cf Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sun, 19 Oct 2025 17:59:34 +0200 Subject: [PATCH 26/30] fix phpcs run on core --- .github/workflows/testing.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index f5cdaa38..b18827fb 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -82,4 +82,8 @@ jobs: # ignored temporarily, add them with the --ignore option. run: | cd drupal/core + # Fix Drupal core PHPCS config by replacing removed sniffs. + sed -i 's/Drupal.Classes.ClassCreateInstance/SlevomatCodingStandard.ControlStructures.NewWithParentheses/g' phpcs.xml.dist + sed -i 's/Drupal.Classes.UnusedUseStatement/SlevomatCodingStandard.Namespaces.UnusedUses/g' phpcs.xml.dist + sed -i '//a \ ' phpcs.xml.dist ../../vendor/bin/phpcs -p -s --parallel=$(nproc) From 2ccf433bb23381c9cc343b233dfb46ec247d387c Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sun, 19 Oct 2025 18:07:52 +0200 Subject: [PATCH 27/30] fix var type checking --- .github/workflows/testing.yml | 2 +- .../Sniffs/Commenting/VariableCommentSniff.php | 2 +- tests/Drupal/good/good.php | 12 ++++++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index b18827fb..a54a2f22 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -86,4 +86,4 @@ jobs: sed -i 's/Drupal.Classes.ClassCreateInstance/SlevomatCodingStandard.ControlStructures.NewWithParentheses/g' phpcs.xml.dist sed -i 's/Drupal.Classes.UnusedUseStatement/SlevomatCodingStandard.Namespaces.UnusedUses/g' phpcs.xml.dist sed -i '//a \ ' phpcs.xml.dist - ../../vendor/bin/phpcs -p -s --parallel=$(nproc) + ../../vendor/bin/phpcs -p -s --parallel=$(nproc) --ignore=lib/Drupal/Core/Command/GenerateTheme.php diff --git a/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php b/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php index 0d3ab8af..ac7c6fb2 100644 --- a/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php +++ b/coder_sniffer/Drupal/Sniffs/Commenting/VariableCommentSniff.php @@ -131,7 +131,7 @@ public function processMemberVar(File $phpcsFile, $stackPtr) if ($foundVar === null) { // If there's an inline type argument then you may omit the @var comment. // Check if there's a type between the variable name and the comment end. - if ($phpcsFile->findPrevious([T_STRING], $stackPtr, $commentEnd) !== false) { + if ($phpcsFile->findPrevious(Tokens::NAME_TOKENS, $stackPtr, $commentEnd) !== false) { return; } diff --git a/tests/Drupal/good/good.php b/tests/Drupal/good/good.php index 7c8aa699..a5bf1179 100644 --- a/tests/Drupal/good/good.php +++ b/tests/Drupal/good/good.php @@ -2054,3 +2054,15 @@ function pdo_weird_return_type($param) { * Comments are allowed to end in 3 dots... */ function comment_test_dots() {} + +/** + * Executes the page caching before the main kernel takes over the request. + */ +class PageCache implements HttpKernelInterface { + + /** + * The wrapped HTTP kernel. + */ + protected \Closure $httpKernel; + +} From a08e6739fa69b92e17f13975f7884bdd493b8e8c Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sun, 19 Oct 2025 18:20:04 +0200 Subject: [PATCH 28/30] Fix phpcs ignores in core --- .github/workflows/testing.yml | 1 + tests/Drupal/good/good.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index a54a2f22..46b1353f 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -86,4 +86,5 @@ jobs: sed -i 's/Drupal.Classes.ClassCreateInstance/SlevomatCodingStandard.ControlStructures.NewWithParentheses/g' phpcs.xml.dist sed -i 's/Drupal.Classes.UnusedUseStatement/SlevomatCodingStandard.Namespaces.UnusedUses/g' phpcs.xml.dist sed -i '//a \ ' phpcs.xml.dist + find . -type f -name "*.php" -exec sed -i 's#// phpcs:ignore Drupal.Classes.PropertyDeclaration, Drupal.NamingConventions.ValidVariableName.LowerCamelName, Drupal.Commenting.VariableComment.Missing#// phpcs:ignore Drupal.NamingConventions.ValidVariableName.LowerCamelName,PSR2.Classes.PropertyDeclaration.Underscore#g' {} + ../../vendor/bin/phpcs -p -s --parallel=$(nproc) --ignore=lib/Drupal/Core/Command/GenerateTheme.php diff --git a/tests/Drupal/good/good.php b/tests/Drupal/good/good.php index a5bf1179..43d2ebdd 100644 --- a/tests/Drupal/good/good.php +++ b/tests/Drupal/good/good.php @@ -2065,4 +2065,12 @@ class PageCache implements HttpKernelInterface { */ protected \Closure $httpKernel; + /** + * The entity for this result. + * + * @var \Drupal\Core\Entity\EntityInterface + */ + // phpcs:ignore Drupal.NamingConventions.ValidVariableName.LowerCamelName,PSR2.Classes.PropertyDeclaration.Underscore + public $_entity = NULL; + } From b500e99e3238a07d9a0767908842429c4a28d31f Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sun, 19 Oct 2025 18:32:35 +0200 Subject: [PATCH 29/30] fixing more ignore comments in core --- .github/workflows/testing.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 46b1353f..939b35be 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -87,4 +87,7 @@ jobs: sed -i 's/Drupal.Classes.UnusedUseStatement/SlevomatCodingStandard.Namespaces.UnusedUses/g' phpcs.xml.dist sed -i '//a \ ' phpcs.xml.dist find . -type f -name "*.php" -exec sed -i 's#// phpcs:ignore Drupal.Classes.PropertyDeclaration, Drupal.NamingConventions.ValidVariableName.LowerCamelName, Drupal.Commenting.VariableComment.Missing#// phpcs:ignore Drupal.NamingConventions.ValidVariableName.LowerCamelName,PSR2.Classes.PropertyDeclaration.Underscore#g' {} + + find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreLine#// phpcs:ignore#g' {} + + find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreFile#// phpcs:ignoreFile#g' {} + + find . -type f -exec sed -i '/\/\/ phpcs:ignore/ s/, /,/g' {} + ../../vendor/bin/phpcs -p -s --parallel=$(nproc) --ignore=lib/Drupal/Core/Command/GenerateTheme.php From 6465deea9f82bbf1fe2c9f3eea3a12444ad66a32 Mon Sep 17 00:00:00 2001 From: Klaus Purer Date: Sun, 19 Oct 2025 18:39:52 +0200 Subject: [PATCH 30/30] more ignore replacements --- .github/workflows/testing.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 939b35be..8257b73c 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -90,4 +90,6 @@ jobs: find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreLine#// phpcs:ignore#g' {} + find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreFile#// phpcs:ignoreFile#g' {} + find . -type f -exec sed -i '/\/\/ phpcs:ignore/ s/, /,/g' {} + - ../../vendor/bin/phpcs -p -s --parallel=$(nproc) --ignore=lib/Drupal/Core/Command/GenerateTheme.php + find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreStart#// phpcs:disable#g' {} + + find . -type f -name "*.php" -exec sed -i 's#// @codingStandardsIgnoreEnd#// phpcs:enable#g' {} + + ../../vendor/bin/phpcs -p -s --parallel=$(nproc) --ignore=lib/Drupal/Core/Command/GenerateTheme.php,modules/mysql/tests/src/Kernel/mysql/Console/DbDumpCommandTest.php