From fa76e6e85cf54e5b9ac2d4a185b709e9a2db5631 Mon Sep 17 00:00:00 2001 From: Jesse Donat Date: Sat, 13 Sep 2025 23:28:28 -0500 Subject: [PATCH 1/5] Fork Slevomat's TestCase to fix our test cases --- src/Corpus/Sniffs/TestCase.php | 269 +++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 src/Corpus/Sniffs/TestCase.php diff --git a/src/Corpus/Sniffs/TestCase.php b/src/Corpus/Sniffs/TestCase.php new file mode 100644 index 0000000..c63638b --- /dev/null +++ b/src/Corpus/Sniffs/TestCase.php @@ -0,0 +1,269 @@ +|bool|int|string> $sniffProperties + * @param list $codesToCheck + * @param list $cliArgs + */ + protected static function checkFile( + string $filePath, + array $sniffProperties = [], + array $codesToCheck = [], + array $cliArgs = [] + ) : File { + if( \defined('PHP_CODESNIFFER_CBF') === false ) { + \define('PHP_CODESNIFFER_CBF', false); + } + + $codeSniffer = new Runner; + $codeSniffer->config = new Config(\array_merge([ '-s' ], $cliArgs)); + $codeSniffer->init(); + + if( \count($sniffProperties) > 0 ) { + foreach( $sniffProperties as $name => $value ) { + $sniffProperties[$name] = [ + 'value' => $value, + 'scope' => 'sniff', + ]; + } + + $codeSniffer->ruleset->ruleset[self::getSniffName()]['properties'] = $sniffProperties; + } + + $sniffClassName = static::getSniffClassName(); + /** @var Sniff $sniff */ + $sniff = new $sniffClassName; + + $codeSniffer->ruleset->sniffs = [ $sniffClassName => $sniff ]; + + if( \count($codesToCheck) > 0 ) { + foreach( self::getSniffClassReflection()->getConstants() as $constantName => $constantValue ) { + if( \strpos($constantName, 'CODE_') !== 0 || \in_array($constantValue, $codesToCheck, true) ) { + continue; + } + + $codeSniffer->ruleset->ruleset[\sprintf('%s.%s', self::getSniffName(), $constantValue)]['severity'] = 0; + } + } + + $codeSniffer->ruleset->populateTokenListeners(); + $codeSniffer->config->tabWidth = static::TAB_WIDTH; + + $file = new LocalFile($filePath, $codeSniffer->ruleset, $codeSniffer->config); + $file->process(); + + return $file; + } + + protected static function assertNoSniffErrorInFile( File $phpcsFile ) : void { + $errors = $phpcsFile->getErrors(); + $text = \sprintf('No errors expected, but %d errors found:', \count($errors)); + foreach( $errors as $line => $error ) { + $text .= \sprintf( + '%sLine %d:%s%s', + \PHP_EOL, + $line, + \PHP_EOL, + self::getFormattedErrors($error), + ); + } + + self::assertEmpty($errors, $text); + } + + protected static function assertNoSniffWarningInFile( File $phpcsFile ) : void { + $warnings = $phpcsFile->getWarnings(); + $text = \sprintf('No warnings expected, but %d warnings found:', \count($warnings)); + foreach( $warnings as $line => $warning ) { + $text .= \sprintf( + '%sLine %d:%s%s', + \PHP_EOL, + $line, + \PHP_EOL, + self::getFormattedErrors($warning), + ); + } + + self::assertEmpty($warnings, $text); + } + + protected static function assertSniffError( File $phpcsFile, int $line, string $code, ?string $message = null ) : void { + $errors = $phpcsFile->getErrors(); + self::assertTrue(isset($errors[$line]), \sprintf('Expected error on line %s, but none found.', $line)); + + $sniffCode = \sprintf('%s.%s', self::getSniffName(), $code); + + self::assertTrue( + self::hasError($errors[$line], $sniffCode, $message), + \sprintf( + 'Expected error %s%s, but none found on line %d.%sErrors found on line %d:%s%s%s', + $sniffCode, + $message !== null + ? \sprintf(' with message "%s"', $message) + : '', + $line, + \PHP_EOL . \PHP_EOL, + $line, + \PHP_EOL, + self::getFormattedErrors($errors[$line]), + \PHP_EOL, + ), + ); + } + + protected static function assertSniffWarning( File $phpcsFile, int $line, string $code, ?string $message = null ) : void { + $errors = $phpcsFile->getWarnings(); + self::assertTrue(isset($errors[$line]), \sprintf('Expected warning on line %s, but none found.', $line)); + + $sniffCode = \sprintf('%s.%s', self::getSniffName(), $code); + + self::assertTrue( + self::hasError($errors[$line], $sniffCode, $message), + \sprintf( + 'Expected warning %s%s, but none found on line %d.%sWarnings found on line %d:%s%s%s', + $sniffCode, + $message !== null + ? \sprintf(' with message "%s"', $message) + : '', + $line, + \PHP_EOL . \PHP_EOL, + $line, + \PHP_EOL, + self::getFormattedErrors($errors[$line]), + \PHP_EOL, + ), + ); + } + + protected static function assertNoSniffError( File $phpcsFile, int $line ) : void { + $errors = $phpcsFile->getErrors(); + self::assertFalse( + isset($errors[$line]), + \sprintf( + 'Expected no error on line %s, but found:%s%s%s', + $line, + \PHP_EOL . \PHP_EOL, + isset($errors[$line]) ? self::getFormattedErrors($errors[$line]) : '', + \PHP_EOL, + ), + ); + } + + protected static function assertAllFixedInFile( File $phpcsFile ) : void { + $phpcsFile->disableCaching(); + $phpcsFile->fixer->fixFile(); + self::assertStringEqualsFile(\preg_replace('~(\\.php)$~', '.fixed\\1', $phpcsFile->getFilename()), $phpcsFile->fixer->getContents()); + } + + /** + * @return class-string + */ + protected static function getSniffClassName() : string { + /** @var class-string $sniffClassName */ + $sniffClassName = \substr(static::class, 0, -\strlen('Test')); + + return $sniffClassName; + } + + protected static function getSniffName() : string { + return Common::getSniffCode(static::getSniffClassName()); + } + + private static function getSniffClassReflection() : \ReflectionClass { + static $reflections = []; + + $className = static::getSniffClassName(); + + return $reflections[$className] ?? $reflections[$className] = new \ReflectionClass($className); + } + + /** + * @param list> $errorsOnLine + */ + private static function hasError( array $errorsOnLine, string $sniffCode, ?string $message ) : bool { + $hasError = false; + + foreach( $errorsOnLine as $errorsOnPosition ) { + foreach( $errorsOnPosition as $error ) { + /** @var string $errorSource */ + $errorSource = $error['source']; + /** @var string $errorMessage */ + $errorMessage = $error['message']; + + if( + $errorSource === $sniffCode + && ( + $message === null + || \strpos($errorMessage, $message) !== false + ) + ) { + $hasError = true; + break; + } + } + } + + return $hasError; + } + + /** + * @param list> $errors + */ + private static function getFormattedErrors( array $errors ) : string { + return \implode( + \PHP_EOL, + \array_map( + static fn ( array $errors ) : string => \implode( + \PHP_EOL, + \array_map(static fn ( array $error ) : string => \sprintf("\t%s: %s", $error['source'], $error['message']), $errors), + ), + $errors, + ), + ); + } + +} From 8e9e639d41f2c4abc46b8f7b13b8233a889a2225 Mon Sep 17 00:00:00 2001 From: Jesse Donat Date: Sat, 13 Sep 2025 23:29:03 -0500 Subject: [PATCH 2/5] Use our forked TestCase --- .../Sniffs/ControlStructures/ClosingBraceNewlineSniffTest.php | 2 +- .../Sniffs/ControlStructures/OpeningOneTrueBraceSniffTest.php | 2 +- test/Corpus/Sniffs/General/BinaryOperationNewlineSniffTest.php | 2 +- test/Corpus/Sniffs/General/ReturnTrailingNewlineSniffTest.php | 2 +- test/Corpus/Sniffs/Methods/ClosureSpacingSniffTest.php | 2 +- .../Sniffs/Methods/MethodParameterFormattingSniffTest.php | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/Corpus/Sniffs/ControlStructures/ClosingBraceNewlineSniffTest.php b/test/Corpus/Sniffs/ControlStructures/ClosingBraceNewlineSniffTest.php index 52d4448..6428792 100644 --- a/test/Corpus/Sniffs/ControlStructures/ClosingBraceNewlineSniffTest.php +++ b/test/Corpus/Sniffs/ControlStructures/ClosingBraceNewlineSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\ControlStructures; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class ClosingBraceNewlineSniffTest extends TestCase { diff --git a/test/Corpus/Sniffs/ControlStructures/OpeningOneTrueBraceSniffTest.php b/test/Corpus/Sniffs/ControlStructures/OpeningOneTrueBraceSniffTest.php index a387619..7a90460 100644 --- a/test/Corpus/Sniffs/ControlStructures/OpeningOneTrueBraceSniffTest.php +++ b/test/Corpus/Sniffs/ControlStructures/OpeningOneTrueBraceSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\ControlStructures; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class OpeningOneTrueBraceSniffTest extends TestCase { diff --git a/test/Corpus/Sniffs/General/BinaryOperationNewlineSniffTest.php b/test/Corpus/Sniffs/General/BinaryOperationNewlineSniffTest.php index c9a044e..03722a9 100644 --- a/test/Corpus/Sniffs/General/BinaryOperationNewlineSniffTest.php +++ b/test/Corpus/Sniffs/General/BinaryOperationNewlineSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\General; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class BinaryOperationNewlineSniffTest extends TestCase { diff --git a/test/Corpus/Sniffs/General/ReturnTrailingNewlineSniffTest.php b/test/Corpus/Sniffs/General/ReturnTrailingNewlineSniffTest.php index 476c426..99bb51b 100644 --- a/test/Corpus/Sniffs/General/ReturnTrailingNewlineSniffTest.php +++ b/test/Corpus/Sniffs/General/ReturnTrailingNewlineSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\General; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class ReturnTrailingNewlineSniffTest extends TestCase { diff --git a/test/Corpus/Sniffs/Methods/ClosureSpacingSniffTest.php b/test/Corpus/Sniffs/Methods/ClosureSpacingSniffTest.php index 0480e27..907a698 100644 --- a/test/Corpus/Sniffs/Methods/ClosureSpacingSniffTest.php +++ b/test/Corpus/Sniffs/Methods/ClosureSpacingSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\Methods; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class ClosureSpacingSniffTest extends TestCase { diff --git a/test/Corpus/Sniffs/Methods/MethodParameterFormattingSniffTest.php b/test/Corpus/Sniffs/Methods/MethodParameterFormattingSniffTest.php index 70aca59..dd8ec03 100644 --- a/test/Corpus/Sniffs/Methods/MethodParameterFormattingSniffTest.php +++ b/test/Corpus/Sniffs/Methods/MethodParameterFormattingSniffTest.php @@ -2,7 +2,7 @@ namespace Corpus\Sniffs\Methods; -use SlevomatCodingStandard\Sniffs\TestCase; +use Corpus\Sniffs\TestCase; class MethodParameterFormattingSniffTest extends TestCase { From 07382df1e4a4761ec3b85f57a99a1639abf97489 Mon Sep 17 00:00:00 2001 From: Jesse Donat Date: Wed, 17 Sep 2025 10:06:22 -0500 Subject: [PATCH 3/5] Upgrade squizlabs/php_codesniffer to 4.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 33f53ff..9323772 100644 --- a/composer.json +++ b/composer.json @@ -9,7 +9,7 @@ "type": "phpcodesniffer-standard", "require": { "dealerdirect/phpcodesniffer-composer-installer": "*", - "squizlabs/php_codesniffer": "^3.9.0", + "squizlabs/php_codesniffer": "^4.0", "slevomat/coding-standard": "^8.14", "php": ">=7.4" }, From 141fce33853d9c5cb5061b2bb9e4ab95c4f3a844 Mon Sep 17 00:00:00 2001 From: Jesse Donat Date: Sat, 13 Sep 2025 23:31:06 -0500 Subject: [PATCH 4/5] Update docs --- README.md | 2 +- src/Corpus/Sniffs/TestCase.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 428a875..4861e91 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Corpus Coding Standard for [PHP_CodeSniffer](https://github.com/squizlabs/PHP_Co ## Requirements - **dealerdirect/phpcodesniffer-composer-installer**: * -- **squizlabs/php_codesniffer**: ^3.9.0 +- **squizlabs/php_codesniffer**: ^4.0 - **slevomat/coding-standard**: ^8.14 - **php**: >=7.4 diff --git a/src/Corpus/Sniffs/TestCase.php b/src/Corpus/Sniffs/TestCase.php index c63638b..f610ed6 100644 --- a/src/Corpus/Sniffs/TestCase.php +++ b/src/Corpus/Sniffs/TestCase.php @@ -38,6 +38,7 @@ use PHP_CodeSniffer\Util\Common; /** + * @mddoc-ignore * @codeCoverageIgnore */ abstract class TestCase extends \PHPUnit\Framework\TestCase { From 4f9370c72cc2a28ab0c03730d43fbfd5401326ee Mon Sep 17 00:00:00 2001 From: Jesse Donat Date: Sat, 13 Sep 2025 23:32:15 -0500 Subject: [PATCH 5/5] Test in PHP 8.5 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24ea01e..f563f52 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: matrix: operating-system: [ubuntu-latest] - php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4'] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5'] runs-on: ${{ matrix.operating-system }}