From bf35750293059c970dc063849ec733f3f7340845 Mon Sep 17 00:00:00 2001 From: Mickael NOEL Date: Wed, 15 Apr 2026 16:40:47 +0200 Subject: [PATCH] feat: add PHP_CodeSniffer 4.x support Upgrade squizlabs/php_codesniffer from ^3.13.5 to ^4.0 and slevomat/coding-standard from ^8.22.1 to ^8.23. Changes: - File.php: typed addMessage() signature, fixableCount replaced by fixableErrorCount/fixableWarningCount - SniffDecorator.php: typed process() parameter - ForbiddenSetterSniff.php: typed process() parameter, dotted error code to comply with PHPCS 4 naming validation, getDeclarationName empty string check - Metrics: removed CodeAnalyzerSniff (dropped from Zend in PHPCS 4, redundant with SyntaxCheck), removed CallTimePassByReferenceSniff (dropped, irrelevant since PHP 5.4), moved LanguageConstructSpacingSniff from Squiz to Generic namespace - TestCase: manual sniff registration to bypass PHPCS 4 namespace convention validation for custom sniffs Closes #723 --- composer.json | 4 +-- src/Domain/File.php | 22 +++++++----- src/Domain/Insights/SniffDecorator.php | 7 +--- src/Domain/Metrics/Code/Code.php | 4 +-- src/Domain/Metrics/Code/Functions.php | 2 -- src/Domain/Metrics/Style/Style.php | 2 +- src/Domain/Sniffs/ForbiddenSetterSniff.php | 13 ++----- tests/Feature/Fix/Fixtures/ParamTypeHint.php | 6 +--- tests/Feature/Fix/Fixtures/UnorderedUse.php | 2 +- tests/TestCase.php | 37 ++++++++++++-------- 10 files changed, 45 insertions(+), 54 deletions(-) diff --git a/composer.json b/composer.json index 3fee96c17..238f907a8 100644 --- a/composer.json +++ b/composer.json @@ -30,8 +30,8 @@ "psr/container": "^2.0.2", "psr/simple-cache": "^2.0|^3.0", "sebastian/diff": "^6.0.2|^7.0.0", - "slevomat/coding-standard": "^8.22.1", - "squizlabs/php_codesniffer": "^3.13.5", + "slevomat/coding-standard": "^8.23", + "squizlabs/php_codesniffer": "^4.0", "symfony/cache": "^7.4.5|^8.0.8", "symfony/console": "^7.4.4|^8.0.8", "symfony/finder": "^7.4.5|^8.0.8", diff --git a/src/Domain/File.php b/src/Domain/File.php index 6d59171d9..a0d6bc7fc 100644 --- a/src/Domain/File.php +++ b/src/Domain/File.php @@ -100,14 +100,14 @@ public function disableFix(): void * {@inheritdoc} */ protected function addMessage( - $isError, - $message, - $line, - $column, - $sniffClassOrCode, - $data, - $severity, - $isFixable = false + bool $isError, + string $message, + int $line, + int $column, + string $sniffClassOrCode, + array $data, + int $severity, + bool $isFixable = false ): bool { $message = $data !== [] ? vsprintf($message, $data) : $message; @@ -115,7 +115,11 @@ protected function addMessage( if ($this->fixEnabled) { $this->activeSniff->addFileFixed($this->fileInfo->getRelativePathname()); } else { - $this->fixableCount++; + if ($isError) { + $this->fixableErrorCount++; + } else { + $this->fixableWarningCount++; + } } return true; diff --git a/src/Domain/Insights/SniffDecorator.php b/src/Domain/Insights/SniffDecorator.php index 3dc2bff59..fb0f7d25a 100644 --- a/src/Domain/Insights/SniffDecorator.php +++ b/src/Domain/Insights/SniffDecorator.php @@ -57,12 +57,7 @@ public function register(): array return $this->sniff->register(); } - /** - * @param int $stackPtr - * - * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint - */ - public function process(File $file, $stackPtr): int|null + public function process(File $file, int $stackPtr): int|null { if ($file instanceof InsightFile && $this->skipFilesFromIgnoreFiles($file)) { return null; diff --git a/src/Domain/Metrics/Code/Code.php b/src/Domain/Metrics/Code/Code.php index 39e65da56..a98f3614f 100644 --- a/src/Domain/Metrics/Code/Code.php +++ b/src/Domain/Metrics/Code/Code.php @@ -23,9 +23,8 @@ use PHP_CodeSniffer\Standards\Generic\Sniffs\Strings\UnnecessaryStringConcatSniff; use PHP_CodeSniffer\Standards\PSR12\Sniffs\Keywords\ShortFormTypeKeywordsSniff; use PHP_CodeSniffer\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff; +use PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\LanguageConstructSpacingSniff; use PHP_CodeSniffer\Standards\Squiz\Sniffs\PHP\EvalSniff; -use PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace\LanguageConstructSpacingSniff; -use PHP_CodeSniffer\Standards\Zend\Sniffs\Debug\CodeAnalyzerSniff; use PhpCsFixer\Fixer\Alias\NoMixedEchoPrintFixer; use PhpCsFixer\Fixer\ArrayNotation\NoMultilineWhitespaceAroundDoubleArrowFixer; use PhpCsFixer\Fixer\ArrayNotation\NormalizeIndexBraceFixer; @@ -80,7 +79,6 @@ public function getInsights(): array { return [ UnusedVariableSniff::class, - CodeAnalyzerSniff::class, SwitchDeclarationSniff::class, LanguageConstructSpacingSniff::class, UselessVariableSniff::class, diff --git a/src/Domain/Metrics/Code/Functions.php b/src/Domain/Metrics/Code/Functions.php index 87cddb0d7..315a03da4 100644 --- a/src/Domain/Metrics/Code/Functions.php +++ b/src/Domain/Metrics/Code/Functions.php @@ -10,7 +10,6 @@ use NunoMaduro\PhpInsights\Domain\Contracts\HasPercentage; use NunoMaduro\PhpInsights\Domain\Contracts\HasValue; use NunoMaduro\PhpInsights\Domain\Insights\ForbiddenDefineFunctions; -use PHP_CodeSniffer\Standards\Generic\Sniffs\Functions\CallTimePassByReferenceSniff; use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\DeprecatedFunctionsSniff; use PHP_CodeSniffer\Standards\Generic\Sniffs\PHP\ForbiddenFunctionsSniff; use PHP_CodeSniffer\Standards\PSR12\Sniffs\Functions\NullableTypeDeclarationSniff; @@ -46,7 +45,6 @@ public function getInsights(): array return [ UnusedInheritedVariablePassedToClosureSniff::class, UnusedParameterSniff::class, - CallTimePassByReferenceSniff::class, DeprecatedFunctionsSniff::class, NullableTypeDeclarationSniff::class, StaticClosureSniff::class, diff --git a/src/Domain/Metrics/Style/Style.php b/src/Domain/Metrics/Style/Style.php index 20a334d1a..c2f81b1cd 100644 --- a/src/Domain/Metrics/Style/Style.php +++ b/src/Domain/Metrics/Style/Style.php @@ -36,7 +36,7 @@ use PHP_CodeSniffer\Standards\PSR2\Sniffs\Files\ClosingTagSniff; use PHP_CodeSniffer\Standards\PSR2\Sniffs\Files\EndFileNewlineSniff; use PHP_CodeSniffer\Standards\PSR2\Sniffs\Methods\FunctionClosingBraceSniff; -use PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace\LanguageConstructSpacingSniff; +use PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\LanguageConstructSpacingSniff; use PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace\SuperfluousWhitespaceSniff; use PhpCsFixer\Fixer\ArrayNotation\NoTrailingCommaInSinglelineArrayFixer; use PhpCsFixer\Fixer\ArrayNotation\NoWhitespaceBeforeCommaInArrayFixer; diff --git a/src/Domain/Sniffs/ForbiddenSetterSniff.php b/src/Domain/Sniffs/ForbiddenSetterSniff.php index e3842148c..64ca15380 100644 --- a/src/Domain/Sniffs/ForbiddenSetterSniff.php +++ b/src/Domain/Sniffs/ForbiddenSetterSniff.php @@ -28,17 +28,10 @@ public function register(): array return [T_FUNCTION]; } - /** - * Runs the sniff on a file. - * - * @param int $position - * - * @phpcsSuppress SlevomatCodingStandard.TypeHints.ParameterTypeHint.MissingNativeTypeHint - */ - public function process(File $file, $position): void + public function process(File $file, int $position): void { $methodName = $file->getDeclarationName($position); - if ($methodName === null) { + if ($methodName === '' || $methodName === null) { return; } @@ -52,7 +45,7 @@ public function process(File $file, $position): void return; } - $file->addError(self::ERROR_MESSAGE, $position, self::class); + $file->addError(self::ERROR_MESSAGE, $position, 'PhpInsights.Sniffs.ForbiddenSetter'); } /** diff --git a/tests/Feature/Fix/Fixtures/ParamTypeHint.php b/tests/Feature/Fix/Fixtures/ParamTypeHint.php index e09814dec..2087b3805 100644 --- a/tests/Feature/Fix/Fixtures/ParamTypeHint.php +++ b/tests/Feature/Fix/Fixtures/ParamTypeHint.php @@ -9,12 +9,8 @@ final class ParamTypeHint { /** * Do some calculation - * @param int $a - * @param int $b - * - * @return int */ - public function sum($a, $b) + public function sum(int $a, int $b): int { return $a + $b; } diff --git a/tests/Feature/Fix/Fixtures/UnorderedUse.php b/tests/Feature/Fix/Fixtures/UnorderedUse.php index d8f746220..0beea64ac 100644 --- a/tests/Feature/Fix/Fixtures/UnorderedUse.php +++ b/tests/Feature/Fix/Fixtures/UnorderedUse.php @@ -2,8 +2,8 @@ declare(strict_types=1); -use NunoMaduro\PhpInsights\Domain\Analyser; use NunoMaduro\PhpInsights\Application\ConfigResolver; +use NunoMaduro\PhpInsights\Domain\Analyser; /** * This test class is for testing if fix from CSFixer is correctly applied diff --git a/tests/TestCase.php b/tests/TestCase.php index 7e4bbce61..e4f0e8f44 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -130,17 +130,9 @@ final public static function prepareFixtureWithSniff( string $fixtureFile, array $properties = [] ): LocalFile { - $sniffs = [self::getFilePathFromClass($sniffClassName)]; - $config = new Config(); $config->standards = []; - $ruleName = str_replace( - 'Sniff', - '', - class_basename($sniffClassName) - ); - /** @var Ruleset $ruleset */ $ruleset = (new ReflectionClass(Ruleset::class)) ->newInstanceWithoutConstructor(); @@ -148,14 +140,29 @@ class_basename($sniffClassName) $msgCacheProperty = (new ReflectionClass(Ruleset::class))->getProperty('msgCache'); $msgCacheProperty->setValue($ruleset, new MessageCollector()); - $ruleset->ruleset = [ - "PhpInsights.Sniffs.{$ruleName}" => [ - 'properties' => $properties, - ], - ]; + // Manually register the sniff to bypass PHPCS naming convention validation + // which rejects sniffs outside the Standard\Sniffs\Category\SniffName namespace + $sniffObject = new $sniffClassName(); + + foreach ($properties as $name => $value) { + $sniffObject->{$name} = $value; + } + + $sniffCode = 'PhpInsights.Sniffs.' . str_replace('Sniff', '', class_basename($sniffClassName)); - $ruleset->registerSniffs($sniffs, [], []); - $ruleset->populateTokenListeners(); + $ruleset->sniffs[$sniffClassName] = $sniffObject; + $ruleset->sniffCodes[$sniffCode] = $sniffClassName; + $ruleset->tokenListeners = []; + + foreach ($sniffObject->register() as $token) { + $ruleset->tokenListeners[$token][$sniffClassName] = [ + 'class' => $sniffClassName, + 'source' => $sniffCode, + 'tokenizers' => ['PHP'], + 'ignore' => [], + 'include' => [], + ]; + } return new LocalFile($fixtureFile, $ruleset, $config); }