From f735d4e1b481249beffbf6877e3a4dc769cd4b52 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 18 Sep 2024 13:24:18 +0200 Subject: [PATCH 01/99] Config: update version nr to next --- src/Config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Config.php b/src/Config.php index 4e4c68f083..f3682ac01d 100644 --- a/src/Config.php +++ b/src/Config.php @@ -85,7 +85,7 @@ class Config * * @var string */ - const VERSION = '3.10.3'; + const VERSION = '3.11.0'; /** * Package stability; either stable, beta or alpha. From 15b5e02ba13cd5b018c9d4b4908f4fade6830ade Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 18 Sep 2024 18:10:00 +0200 Subject: [PATCH 02/99] PHP 8.4 | build-phar: remove use of `E_STRICT` The `E_STRICT` constant is deprecated as of PHP 8.4 and will be removed in PHP 9.0. The error level hasn't been in use since PHP 8.0 anyway, so removing the exclusion from the `error_reporting()` setting in the `build-phar` script should make no difference in practice. Ref: * https://wiki.php.net/rfc/deprecations_php_8_4#remove_e_strict_error_level_and_deprecate_e_strict_constant --- scripts/build-phar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build-phar.php b/scripts/build-phar.php index f9621cbaef..57bf3fd2d3 100644 --- a/scripts/build-phar.php +++ b/scripts/build-phar.php @@ -19,7 +19,7 @@ use PHP_CodeSniffer\Tokenizers\PHP; use PHP_CodeSniffer\Util\Tokens; -error_reporting(E_ALL | E_STRICT); +error_reporting(E_ALL); if (ini_get('phar.readonly') === '1') { echo 'Unable to build, phar.readonly in php.ini is set to read only.'.PHP_EOL; From 46259381b84d3f858e59a0e46e3d5eda64a8343a Mon Sep 17 00:00:00 2001 From: Dan Wallis Date: Mon, 29 Jul 2024 23:15:20 +0100 Subject: [PATCH 03/99] Correct error code in Squiz.ControlStructures.ForEachLoopDeclaration From what we can determine, this is a typographical error / mistake. It seems that the intention here was to use the same error code for both sides of this if/else block. --- .../Sniffs/ControlStructures/ForEachLoopDeclarationSniff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Standards/Squiz/Sniffs/ControlStructures/ForEachLoopDeclarationSniff.php b/src/Standards/Squiz/Sniffs/ControlStructures/ForEachLoopDeclarationSniff.php index bd51b265f4..456886b369 100644 --- a/src/Standards/Squiz/Sniffs/ControlStructures/ForEachLoopDeclarationSniff.php +++ b/src/Standards/Squiz/Sniffs/ControlStructures/ForEachLoopDeclarationSniff.php @@ -90,7 +90,7 @@ public function process(File $phpcsFile, $stackPtr) $this->requiredSpacesAfterOpen, $spaceAfterOpen, ]; - $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpacingAfterOpen', $data); + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceAfterOpen', $data); if ($fix === true) { $padding = str_repeat(' ', $this->requiredSpacesAfterOpen); if ($spaceAfterOpen === 0) { From 40f1a5513399aef5da01dfb280e06e7e39330562 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 31 Jul 2024 05:20:50 +0200 Subject: [PATCH 04/99] :sparkles: New `Generic.WhiteSpace.HereNowdocIdentifierSpacing` sniff New `Generic.WhiteSpace.HereNowdocIdentifierSpacing` sniff which forbids whitespace between the `<<<` and the identifier string in heredoc/nowdoc start tokens. Includes fixer. Includes tests. Includes XML docs. --- .../HereNowdocIdentifierSpacingStandard.xml | 23 +++++++ .../HereNowdocIdentifierSpacingSniff.php | 66 +++++++++++++++++++ .../HereNowdocIdentifierSpacingUnitTest.inc | 25 +++++++ ...eNowdocIdentifierSpacingUnitTest.inc.fixed | 25 +++++++ .../HereNowdocIdentifierSpacingUnitTest.php | 58 ++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 src/Standards/Generic/Docs/WhiteSpace/HereNowdocIdentifierSpacingStandard.xml create mode 100644 src/Standards/Generic/Sniffs/WhiteSpace/HereNowdocIdentifierSpacingSniff.php create mode 100644 src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.inc create mode 100644 src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.inc.fixed create mode 100644 src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.php diff --git a/src/Standards/Generic/Docs/WhiteSpace/HereNowdocIdentifierSpacingStandard.xml b/src/Standards/Generic/Docs/WhiteSpace/HereNowdocIdentifierSpacingStandard.xml new file mode 100644 index 0000000000..17cad50f69 --- /dev/null +++ b/src/Standards/Generic/Docs/WhiteSpace/HereNowdocIdentifierSpacingStandard.xml @@ -0,0 +1,23 @@ + + + + + + + << +some text +EOD; + ]]> + + + <<< END +some text +END; + ]]> + + + diff --git a/src/Standards/Generic/Sniffs/WhiteSpace/HereNowdocIdentifierSpacingSniff.php b/src/Standards/Generic/Sniffs/WhiteSpace/HereNowdocIdentifierSpacingSniff.php new file mode 100644 index 0000000000..2296525f3f --- /dev/null +++ b/src/Standards/Generic/Sniffs/WhiteSpace/HereNowdocIdentifierSpacingSniff.php @@ -0,0 +1,66 @@ + + * @copyright 2024 PHPCSStandards and contributors + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace; + +use PHP_CodeSniffer\Files\File; +use PHP_CodeSniffer\Sniffs\Sniff; + +class HereNowdocIdentifierSpacingSniff implements Sniff +{ + + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() + { + return [ + T_START_HEREDOC, + T_START_NOWDOC, + ]; + + }//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(); + + if (strpos($tokens[$stackPtr]['content'], ' ') === false + && strpos($tokens[$stackPtr]['content'], "\t") === false + ) { + // Nothing to do. + return; + } + + $error = 'There should be no space between the <<< and the heredoc/nowdoc identifier string'; + $data = [$tokens[$stackPtr]['content']]; + + $fix = $phpcsFile->addFixableError($error, $stackPtr, 'SpaceFound', $data); + if ($fix === true) { + $replacement = str_replace([' ', "\t"], '', $tokens[$stackPtr]['content']); + $phpcsFile->fixer->replaceToken($stackPtr, $replacement); + } + + }//end process() + + +}//end class diff --git a/src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.inc b/src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.inc new file mode 100644 index 0000000000..0121118a57 --- /dev/null +++ b/src/Standards/Generic/Tests/WhiteSpace/HereNowdocIdentifierSpacingUnitTest.inc @@ -0,0 +1,25 @@ + + * @copyright 2024 PHPCSStandards and contributors + * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence + */ + +namespace PHP_CodeSniffer\Standards\Generic\Tests\WhiteSpace; + +use PHP_CodeSniffer\Tests\Standards\AbstractSniffUnitTest; + +/** + * Unit test class for the LanguageConstructSpacing sniff. + * + * @covers \PHP_CodeSniffer\Standards\Generic\Sniffs\WhiteSpace\HereNowdocIdentifierSpacingSniff + */ +final class HereNowdocIdentifierSpacingUnitTest extends AbstractSniffUnitTest +{ + + + /** + * Returns the lines where errors should occur. + * + * The key of the array should represent the line number and the value + * should represent the number of errors that should occur on that line. + * + * @return array + */ + public function getErrorList() + { + return [ + 11 => 1, + 15 => 1, + 19 => 1, + 23 => 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. + * + * @return array + */ + public function getWarningList() + { + return []; + + }//end getWarningList() + + +}//end class From a57d5f956bb866a5c3aeaa58e51e4c81d902e513 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 29 Sep 2024 23:58:25 +0200 Subject: [PATCH 05/99] README/Changelog: fix a few URLs which have changed Fix a few URLs which were being redirected. Includes: * Replacing badges from poser.pugx.org with badges using img.shields.io. * Adding PHP 8.4 to the "tested on" badge. --- CHANGELOG.md | 2 +- README.md | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 777e59b795..77ea3f1c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,7 +48,7 @@ _Nothing yet._ [#608]: https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/608 [ghcli]: https://cli.github.com/ -[ghattest]: https://docs.github.com/en/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds +[ghattest]: https://docs.github.com/en/actions/security-for-github-actions/using-artifact-attestations/using-artifact-attestations-to-establish-provenance-for-builds ## [3.10.2] - 2024-07-22 diff --git a/README.md b/README.md index 6c7c073beb..207cf226a7 100644 --- a/README.md +++ b/README.md @@ -2,14 +2,14 @@ '.PHP_EOL; + echo ' '.PHP_EOL; + echo ''.PHP_EOL; + } + + /** + * Print the _real_ footer of the HTML page. + * + * @return void + */ + public function printRealFooter() + { + parent::printFooter(); + } +} diff --git a/tests/Core/Generators/Fixtures/MarkdownDouble.php b/tests/Core/Generators/Fixtures/MarkdownDouble.php new file mode 100644 index 0000000000..6f51f3ec4c --- /dev/null +++ b/tests/Core/Generators/Fixtures/MarkdownDouble.php @@ -0,0 +1,36 @@ +getTitle($doc), PHP_EOL; + } +} diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoContentStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoContentStandard.xml new file mode 100644 index 0000000000..83afee8e83 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoContentStandard.xml @@ -0,0 +1,2 @@ + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoDocumentationElementStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoDocumentationElementStandard.xml new file mode 100644 index 0000000000..ca8290f1b4 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/NoDocumentationElementStandard.xml @@ -0,0 +1,2 @@ + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneCodeComparisonNoStandardStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneCodeComparisonNoStandardStandard.xml new file mode 100644 index 0000000000..c2af9098a5 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneCodeComparisonNoStandardStandard.xml @@ -0,0 +1,14 @@ + + + + class Code {} + ]]> + + + class Comparison {} + ]]> + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockCodeComparisonStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockCodeComparisonStandard.xml new file mode 100644 index 0000000000..c3ce35cd71 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockCodeComparisonStandard.xml @@ -0,0 +1,19 @@ + + + + + + + class Code {} + ]]> + + + class Comparison {} + ]]> + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockNoCodeStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockNoCodeStandard.xml new file mode 100644 index 0000000000..fc014949e7 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockNoCodeStandard.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockTwoCodeComparisonsStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockTwoCodeComparisonsStandard.xml new file mode 100644 index 0000000000..19559e6720 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/OneStandardBlockTwoCodeComparisonsStandard.xml @@ -0,0 +1,31 @@ + + + + + + + class Code {} + ]]> + + + class Comparison {} + ]]> + + + + + $one = 10; + ]]> + + + $a = 10; + ]]> + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksNoCodeStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksNoCodeStandard.xml new file mode 100644 index 0000000000..f5f621ecdc --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksNoCodeStandard.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksOneCodeComparisonStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksOneCodeComparisonStandard.xml new file mode 100644 index 0000000000..a5b3a3216e --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksOneCodeComparisonStandard.xml @@ -0,0 +1,24 @@ + + + + + + + Code {} + ]]> + + + Comparison {} + ]]> + + + + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksThreeCodeComparisonsStandard.xml b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksThreeCodeComparisonsStandard.xml new file mode 100644 index 0000000000..540ac7eaf7 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Docs/Structure/TwoStandardBlocksThreeCodeComparisonsStandard.xml @@ -0,0 +1,48 @@ + + + + + + + class Code {} + ]]> + + + class Comparison {} + ]]> + + + + + + + + $one = 10; + ]]> + + + $a = 10; + ]]> + + + + + echo $foo; + ]]> + + + print $foo; + ]]> + + + diff --git a/tests/Core/Generators/Fixtures/StandardWithDocs/Sniffs/DummySniff.php b/tests/Core/Generators/Fixtures/StandardWithDocs/Sniffs/DummySniff.php new file mode 100644 index 0000000000..1a2546b111 --- /dev/null +++ b/tests/Core/Generators/Fixtures/StandardWithDocs/Sniffs/DummySniff.php @@ -0,0 +1,25 @@ + + + + diff --git a/tests/Core/Generators/GeneratorTest.php b/tests/Core/Generators/GeneratorTest.php new file mode 100644 index 0000000000..30536518f6 --- /dev/null +++ b/tests/Core/Generators/GeneratorTest.php @@ -0,0 +1,241 @@ + $expected The expected list of found docs. + * + * @dataProvider dataConstructor + * + * @return void + */ + public function testConstructor($standard, array $expected) + { + // Set up the ruleset. + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $generator = new MockGenerator($ruleset); + $this->assertSame($expected, $generator->docFiles); + + }//end testConstructor() + + + /** + * Data provider. + * + * @return array>> + */ + public static function dataConstructor() + { + $pathToDocsInFixture = __DIR__.DIRECTORY_SEPARATOR.'Fixtures'; + $pathToDocsInFixture .= DIRECTORY_SEPARATOR.'StandardWithDocs'; + $pathToDocsInFixture .= DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR; + + return [ + 'Standard without docs' => [ + 'standard' => __DIR__.'/NoDocsTest.xml', + 'expected' => [], + ], + 'Standard with an invalid doc file' => [ + 'standard' => __DIR__.'/NoValidDocsTest.xml', + 'expected' => [ + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'NoDocumentationElementStandard.xml', + ], + ], + 'Standard with one doc file' => [ + 'standard' => __DIR__.'/OneDocTest.xml', + 'expected' => [ + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'OneStandardBlockNoCodeStandard.xml', + ], + ], + 'Standard with multiple doc files' => [ + 'standard' => __DIR__.'/StructureDocsTest.xml', + 'expected' => [ + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'NoContentStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'OneCodeComparisonNoStandardStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'OneStandardBlockCodeComparisonStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'OneStandardBlockNoCodeStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'OneStandardBlockTwoCodeComparisonsStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'TwoStandardBlocksNoCodeStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'TwoStandardBlocksOneCodeComparisonStandard.xml', + $pathToDocsInFixture.'Structure'.DIRECTORY_SEPARATOR.'TwoStandardBlocksThreeCodeComparisonsStandard.xml', + ], + ], + ]; + + }//end dataConstructor() + + + /** + * Verify that an XML doc which isn't valid documentation yields an Exception to warn devs. + * + * This should not be hidden via defensive coding! + * + * @return void + */ + public function testGeneratingInvalidDocsResultsInException() + { + // Set up the ruleset. + $standard = __DIR__.'/NoValidDocsTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + if (PHP_VERSION_ID >= 80000) { + $exception = 'TypeError'; + $message = 'processSniff(): Argument #1 ($doc) must be of type DOMNode, null given'; + } else if (PHP_VERSION_ID >= 70000) { + $exception = 'TypeError'; + $message = 'processSniff() must be an instance of DOMNode, null given'; + } else { + $exception = 'PHPUnit_Framework_Error'; + $message = 'processSniff() must be an instance of DOMNode, null given'; + } + + if (method_exists($this, 'expectExceptionMessage') === true) { + // PHPUnit 5.2.0+. + $this->expectException($exception); + $this->expectExceptionMessage($message); + } else { + // Ancient PHPUnit. + $this->setExpectedException($exception, $message); + } + + $generator = new MockGenerator($ruleset); + $generator->generate(); + + }//end testGeneratingInvalidDocsResultsInException() + + + /** + * Verify the wiring for the generate() function. + * + * @param string $standard The standard to use for the test. + * @param string $expected The expected function output. + * + * @dataProvider dataGeneratingDocs + * + * @return void + */ + public function testGeneratingDocs($standard, $expected) + { + // Set up the ruleset. + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $this->expectOutputString($expected); + + $generator = new MockGenerator($ruleset); + $generator->generate(); + + }//end testGeneratingDocs() + + + /** + * Data provider. + * + * @return array> + */ + public static function dataGeneratingDocs() + { + $multidocExpected = []; + $multidocExpected[] = 'No Content'; + $multidocExpected[] = 'Code Comparison Only, Missing Standard Block'; + $multidocExpected[] = 'One Standard Block, Code Comparison'; + $multidocExpected[] = 'One Standard Block, No Code'; + $multidocExpected[] = 'One Standard Block, Two Code Comparisons'; + $multidocExpected[] = 'Two Standard Blocks, No Code'; + $multidocExpected[] = 'Two Standard Blocks, One Code Comparison'; + $multidocExpected[] = 'Two Standard Blocks, Three Code Comparisons'; + $multidocExpected = implode(PHP_EOL, $multidocExpected).PHP_EOL; + + return [ + 'Standard without docs' => [ + 'standard' => __DIR__.'/NoDocsTest.xml', + 'expected' => '', + ], + 'Standard with one doc file' => [ + 'standard' => __DIR__.'/OneDocTest.xml', + 'expected' => 'One Standard Block, No Code'.PHP_EOL, + ], + 'Standard with multiple doc files' => [ + 'standard' => __DIR__.'/StructureDocsTest.xml', + 'expected' => $multidocExpected, + ], + ]; + + }//end dataGeneratingDocs() + + + /** + * Test that the documentation for each standard passed on the command-line is shown separately. + * + * @covers \PHP_CodeSniffer\Runner::runPHPCS + * + * @return void + */ + public function testGeneratorWillShowEachStandardSeparately() + { + $standard = __DIR__.'/OneDocTest.xml'; + $_SERVER['argv'] = [ + 'phpcs', + '--generator=Text', + "--standard=$standard,PSR1", + '--report-width=80', + ]; + + $regex = '`^ + \R* # Optional blank line at the start. + (?: + (?P-+\R) # Line with dashes. + \|[ ]GENERATORTEST[ ]CODING[ ]STANDARD:[ ][^\|]+\|\R # Doc title line with prefix expected for first standard. + (?P>delimiter) # Line with dashes. + .+?\R{2} # Standard description. + ) # Only expect this group once. + (?: + (?P>delimiter) # Line with dashes. + \|[ ]PSR1[ ]CODING[ ]STANDARD:[ ][^\|]+\|\R # Doc title line with prefix expected for second standard. + (?P>delimiter) # Line with dashes. + .+?\R+ # Standard description. + (?: + -+[ ]CODE[ ]COMPARISON[ ]-+\R # Code Comparison starter line with dashes. + (?:.+?(?P>delimiter)\R){2} # Arbitrary text followed by a delimiter line. + )* # Code comparison is optional and can exist multiple times. + \R+ + ){3,} # This complete group should occur at least three times. + `sx'; + + $this->expectOutputRegex($regex); + + $runner = new Runner(); + $runner->runPHPCS(); + + }//end testGeneratorWillShowEachStandardSeparately() + + +}//end class diff --git a/tests/Core/Generators/HTMLTest.php b/tests/Core/Generators/HTMLTest.php new file mode 100644 index 0000000000..4140937ab7 --- /dev/null +++ b/tests/Core/Generators/HTMLTest.php @@ -0,0 +1,104 @@ +assertNotFalse($expected, 'Output expectation file could not be found'); + + // Make the test OS independent. + $expected = str_replace("\n", PHP_EOL, $expected); + $this->expectOutputString($expected); + + $generator = new HTMLDouble($ruleset); + $generator->generate(); + + }//end testDocs() + + + /** + * Data provider. + * + * @return array> + */ + public static function dataDocs() + { + return [ + 'Standard without docs' => [ + 'standard' => __DIR__.'/NoDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputNoDocs.html', + ], + 'Standard with one doc file' => [ + 'standard' => __DIR__.'/OneDocTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputOneDoc.html', + ], + 'Standard with multiple doc files' => [ + 'standard' => __DIR__.'/StructureDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputStructureDocs.html', + ], + ]; + + }//end dataDocs() + + + /** + * Test the generated footer. + * + * @return void + */ + public function testFooter() + { + // Set up the ruleset. + $standard = __DIR__.'/OneDocTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $regex = '`^
'; + $regex .= 'Documentation generated on [A-Z][a-z]{2}, [0-9]{2} [A-Z][a-z]{2} 20[0-9]{2} [0-2][0-9](?::[0-5][0-9]){2} [+-][0-9]{4}'; + $regex .= ' by PHP_CodeSniffer [3-9]\.[0-9]+.[0-9]+'; + $regex .= '
\R \R\R$`'; + $this->expectOutputRegex($regex); + + $generator = new HTMLDouble($ruleset); + $generator->printRealFooter(); + + }//end testFooter() + + +}//end class diff --git a/tests/Core/Generators/MarkdownTest.php b/tests/Core/Generators/MarkdownTest.php new file mode 100644 index 0000000000..9a3e540044 --- /dev/null +++ b/tests/Core/Generators/MarkdownTest.php @@ -0,0 +1,102 @@ +assertNotFalse($expected, 'Output expectation file could not be found'); + + // Make the test OS independent. + $expected = str_replace("\n", PHP_EOL, $expected); + $this->expectOutputString($expected); + + $generator = new MarkdownDouble($ruleset); + $generator->generate(); + + }//end testDocs() + + + /** + * Data provider. + * + * @return array> + */ + public static function dataDocs() + { + return [ + 'Standard without docs' => [ + 'standard' => __DIR__.'/NoDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputNoDocs.md', + ], + 'Standard with one doc file' => [ + 'standard' => __DIR__.'/OneDocTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputOneDoc.md', + ], + 'Standard with multiple doc files' => [ + 'standard' => __DIR__.'/StructureDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputStructureDocs.md', + ], + ]; + + }//end dataDocs() + + + /** + * Test the generated footer. + * + * @return void + */ + public function testFooter() + { + // Set up the ruleset. + $standard = __DIR__.'/OneDocTest.xml'; + $config = new ConfigDouble(["--standard=$standard"]); + $ruleset = new Ruleset($config); + + $regex = '`^Documentation generated on [A-Z][a-z]{2}, [0-9]{2} [A-Z][a-z]{2} 20[0-9]{2} [0-2][0-9](?::[0-5][0-9]){2} [+-][0-9]{4}'; + $regex .= ' by \[PHP_CodeSniffer [3-9]\.[0-9]+.[0-9]+\]\(https://github\.com/PHPCSStandards/PHP_CodeSniffer\)\R$`'; + $this->expectOutputRegex($regex); + + $generator = new MarkdownDouble($ruleset); + $generator->printRealFooter(); + + }//end testFooter() + + +}//end class diff --git a/tests/Core/Generators/NoDocsTest.xml b/tests/Core/Generators/NoDocsTest.xml new file mode 100644 index 0000000000..51df8395c0 --- /dev/null +++ b/tests/Core/Generators/NoDocsTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Core/Generators/NoValidDocsTest.xml b/tests/Core/Generators/NoValidDocsTest.xml new file mode 100644 index 0000000000..94dda4e714 --- /dev/null +++ b/tests/Core/Generators/NoValidDocsTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Core/Generators/OneDocTest.xml b/tests/Core/Generators/OneDocTest.xml new file mode 100644 index 0000000000..83ca4384e5 --- /dev/null +++ b/tests/Core/Generators/OneDocTest.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/tests/Core/Generators/StructureDocsTest.xml b/tests/Core/Generators/StructureDocsTest.xml new file mode 100644 index 0000000000..45811bee67 --- /dev/null +++ b/tests/Core/Generators/StructureDocsTest.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/tests/Core/Generators/TextTest.php b/tests/Core/Generators/TextTest.php new file mode 100644 index 0000000000..40544557f2 --- /dev/null +++ b/tests/Core/Generators/TextTest.php @@ -0,0 +1,80 @@ +assertNotFalse($expected, 'Output expectation file could not be found'); + + // Make the test OS independent. + $expected = str_replace("\n", PHP_EOL, $expected); + $this->expectOutputString($expected); + + $generator = new Text($ruleset); + $generator->generate(); + + }//end testDocs() + + + /** + * Data provider. + * + * @return array> + */ + public static function dataDocs() + { + return [ + 'Standard without docs' => [ + 'standard' => __DIR__.'/NoDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputEmpty.txt', + ], + 'Standard with one doc file' => [ + 'standard' => __DIR__.'/OneDocTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputOneDoc.txt', + ], + 'Standard with multiple doc files' => [ + 'standard' => __DIR__.'/StructureDocsTest.xml', + 'pathToExpected' => __DIR__.'/Expectations/ExpectedOutputStructureDocs.txt', + ], + ]; + + }//end dataDocs() + + +}//end class From 3d64cb24a7a4d78531b91bce41cea8c27736a51c Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 14 Nov 2024 01:29:08 +0100 Subject: [PATCH 95/99] RuleInclusionAbsoluteWindowsTest: minor tweaks * Switching the test skipping from an inline condition to a PHPUnit annotation. * Adding the `@group Windows` annotation to ensure this test will run for code coverage on Windows in CI. --- .../Ruleset/RuleInclusionAbsoluteWindowsTest.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php b/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php index cba45ee399..9dd0370b28 100644 --- a/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php +++ b/tests/Core/Ruleset/RuleInclusionAbsoluteWindowsTest.php @@ -16,7 +16,9 @@ /** * Tests for the \PHP_CodeSniffer\Ruleset class using a Windows-style absolute path to include a sniff. * - * @covers \PHP_CodeSniffer\Ruleset + * @covers \PHP_CodeSniffer\Ruleset + * @requires OS ^WIN.*. + * @group Windows */ final class RuleInclusionAbsoluteWindowsTest extends TestCase { @@ -52,10 +54,6 @@ final class RuleInclusionAbsoluteWindowsTest extends TestCase */ public function initializeConfigAndRuleset() { - if (DIRECTORY_SEPARATOR === '/') { - $this->markTestSkipped('Windows specific test'); - } - $this->standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; $repoRootDir = dirname(dirname(dirname(__DIR__))); @@ -85,9 +83,7 @@ public function initializeConfigAndRuleset() */ public function resetRuleset() { - if (DIRECTORY_SEPARATOR !== '/') { - file_put_contents($this->standard, $this->contents); - } + file_put_contents($this->standard, $this->contents); }//end resetRuleset() From 76927ef412ea9534dc0939698593686f909e5c26 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Mon, 4 Nov 2024 00:50:07 +0100 Subject: [PATCH 96/99] Generators/Generator: minor tweak * Predefine the find/replace values as these don't change within the loop. * Only call `str_replace()` once with arrays for find/replace. --- src/Generators/Generator.php | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Generators/Generator.php b/src/Generators/Generator.php index ab81baed76..873ac3f3fa 100644 --- a/src/Generators/Generator.php +++ b/src/Generators/Generator.php @@ -6,7 +6,9 @@ * in a standard. * * @author Greg Sherwood + * @author Juliette Reinders Folmer * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600) + * @copyright 2024 PHPCSStandards and contributors * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence */ @@ -46,14 +48,18 @@ public function __construct(Ruleset $ruleset) { $this->ruleset = $ruleset; + $find = [ + DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR, + 'Sniff.php', + ]; + $replace = [ + DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR, + 'Standard.xml', + ]; + foreach ($ruleset->sniffs as $className => $sniffClass) { $file = Autoload::getLoadedFileName($className); - $docFile = str_replace( - DIRECTORY_SEPARATOR.'Sniffs'.DIRECTORY_SEPARATOR, - DIRECTORY_SEPARATOR.'Docs'.DIRECTORY_SEPARATOR, - $file - ); - $docFile = str_replace('Sniff.php', 'Standard.xml', $docFile); + $docFile = str_replace($find, $replace, $file); if (is_file($docFile) === true) { $this->docFiles[] = $docFile; From 78d3d3d9db029209ad96c8ec2c7a86c32ddf65b5 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 15 Nov 2024 16:15:31 +0100 Subject: [PATCH 97/99] RuleInclusionTest: record code coverage Code executed during "before class" methods is not recorded for code coverage, while code executed in "before" methods is, but the "before" method is executed before _every_ test in the class, not just once before the tests in the class run. So, to record code coverage, while still maintaining the performance benefits of only creating the Config and Ruleset objects once, the code still sets a static property and will only run if that static property has not been filled yet. --- tests/Core/Ruleset/RuleInclusionTest.php | 38 +++++++++++++----------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/tests/Core/Ruleset/RuleInclusionTest.php b/tests/Core/Ruleset/RuleInclusionTest.php index ef3ad83cac..175febf30a 100644 --- a/tests/Core/Ruleset/RuleInclusionTest.php +++ b/tests/Core/Ruleset/RuleInclusionTest.php @@ -47,35 +47,37 @@ final class RuleInclusionTest extends TestCase /** * Initialize the config and ruleset objects based on the `RuleInclusionTest.xml` ruleset file. * - * @beforeClass + * @before * * @return void */ public static function initializeConfigAndRuleset() { - $standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; - self::$standard = $standard; + if (self::$standard === '') { + $standard = __DIR__.'/'.basename(__FILE__, '.php').'.xml'; + self::$standard = $standard; - // On-the-fly adjust the ruleset test file to be able to test - // sniffs included with relative paths. - $contents = file_get_contents($standard); - self::$contents = $contents; + // On-the-fly adjust the ruleset test file to be able to test + // sniffs included with relative paths. + $contents = file_get_contents($standard); + self::$contents = $contents; - $repoRootDir = basename(dirname(dirname(dirname(__DIR__)))); + $repoRootDir = basename(dirname(dirname(dirname(__DIR__)))); - $newPath = $repoRootDir; - if (DIRECTORY_SEPARATOR === '\\') { - $newPath = str_replace('\\', '/', $repoRootDir); - } + $newPath = $repoRootDir; + if (DIRECTORY_SEPARATOR === '\\') { + $newPath = str_replace('\\', '/', $repoRootDir); + } - $adjusted = str_replace('%path_root_dir%', $newPath, $contents); + $adjusted = str_replace('%path_root_dir%', $newPath, $contents); - if (file_put_contents($standard, $adjusted) === false) { - self::markTestSkipped('On the fly ruleset adjustment failed'); - } + if (file_put_contents($standard, $adjusted) === false) { + self::markTestSkipped('On the fly ruleset adjustment failed'); + } - $config = new ConfigDouble(["--standard=$standard"]); - self::$ruleset = new Ruleset($config); + $config = new ConfigDouble(["--standard=$standard"]); + self::$ruleset = new Ruleset($config); + }//end if }//end initializeConfigAndRuleset() From 7e29324efefc6db1efe3ea0b2e733254f22345d8 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 15 Nov 2024 16:04:43 +0100 Subject: [PATCH 98/99] RuleInclusionTest: add test with directory include --- tests/Core/Ruleset/RuleInclusionTest.php | 10 +++++++++- tests/Core/Ruleset/RuleInclusionTest.xml | 9 +++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/Core/Ruleset/RuleInclusionTest.php b/tests/Core/Ruleset/RuleInclusionTest.php index 175febf30a..3f6a7180d6 100644 --- a/tests/Core/Ruleset/RuleInclusionTest.php +++ b/tests/Core/Ruleset/RuleInclusionTest.php @@ -103,7 +103,7 @@ public function resetRuleset() */ public function testHasSniffCodes() { - $this->assertCount(48, self::$ruleset->sniffCodes); + $this->assertCount(49, self::$ruleset->sniffCodes); }//end testHasSniffCodes() @@ -320,6 +320,10 @@ public static function dataRegisteredSniffCodes() 'Generic.Metrics.CyclomaticComplexity', 'PHP_CodeSniffer\Standards\Generic\Sniffs\Metrics\CyclomaticComplexitySniff', ], + [ + 'Squiz.Files.FileExtension', + 'PHP_CodeSniffer\Standards\Squiz\Sniffs\Files\FileExtensionSniff', + ], [ 'Generic.NamingConventions.CamelCapsFunctionName', 'PHP_CodeSniffer\Standards\Generic\Sniffs\NamingConventions\CamelCapsFunctionNameSniff', @@ -470,6 +474,10 @@ public static function dataSettingInvalidPropertiesOnStandardsAndCategoriesSilen 'sniffClass' => 'PHP_CodeSniffer\Standards\PSR12\Sniffs\Operators\OperatorSpacingSniff', 'propertyName' => 'setforallincategory', ], + 'Set property for all sniffs in included category directory' => [ + 'sniffClass' => 'PHP_CodeSniffer\Standards\Squiz\Sniffs\Files\FileExtensionSniff', + 'propertyName' => 'setforsquizfilessniffs', + ], ]; }//end dataSettingInvalidPropertiesOnStandardsAndCategoriesSilentlyFails() diff --git a/tests/Core/Ruleset/RuleInclusionTest.xml b/tests/Core/Ruleset/RuleInclusionTest.xml index 8275fe0111..6b5c0a970b 100644 --- a/tests/Core/Ruleset/RuleInclusionTest.xml +++ b/tests/Core/Ruleset/RuleInclusionTest.xml @@ -27,6 +27,14 @@ + + + + + + + + @@ -39,6 +47,7 @@ + From 168c0d1bf200c435b47dabcdf59c17bbd17cbe45 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 12 Nov 2024 12:20:43 +0100 Subject: [PATCH 99/99] Changelog for the 3.11.1 release --- CHANGELOG.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e15f42e726..5a4ba6aedf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ The file documents changes to the PHP_CodeSniffer project. _Nothing yet._ +## [3.11.1] - 2024-11-16 + +### Changed +- Output from the `--generator=...` feature will respect the OS-expected EOL char in more places. [#671] + - Thanks to [Juliette Reinders Folmer][@jrfnl] for the patch. +- Various housekeeping, including improvements to the tests and documentation. + - Thanks to [Bartosz DziewoƄski][@MatmaRex] and [Juliette Reinders Folmer][@jrfnl] for their contributions. + +### Fixed +- Fixed bug [#674] : Generic.WhiteSpace.HereNowdocIdentifierSpacing broken XML documentation + - Thanks to [Juliette Reinders Folmer][@jrfnl] for the patch. +- Fixed bug [#675] : InvalidArgumentException when a ruleset includes a sniff by file name and the included sniff does not comply with the PHPCS naming conventions. + - Notwithstanding this fix, it is strongly recommended to ensure custom sniff classes comply with the PHPCS naming conventions. + - Thanks to [Juliette Reinders Folmer][@jrfnl] for the patch. + +[#671]: https://github.com/PHPCSStandards/PHP_CodeSniffer/pull/671 +[#674]: https://github.com/PHPCSStandards/PHP_CodeSniffer/pull/674 +[#675]: https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/675 + ## [3.11.0] - 2024-11-12 ### Added @@ -7133,6 +7152,7 @@ Additionally, thanks to [Alexander Turek][@derrabus] for consulting on the repo --> [Unreleased]: https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/master...HEAD +[3.11.1]: https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.11.0...3.11.1 [3.11.0]: https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.10.3...3.11.0 [3.10.3]: https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.10.2...3.10.3 [3.10.2]: https://github.com/PHPCSStandards/PHP_CodeSniffer/compare/3.10.1...3.10.2 @@ -7370,6 +7390,7 @@ Additionally, thanks to [Alexander Turek][@derrabus] for consulting on the repo [@maryo]: https://github.com/maryo [@MasterOdin]: https://github.com/MasterOdin [@mathroc]: https://github.com/mathroc +[@MatmaRex]: https://github.com/MatmaRex [@maxgalbu]: https://github.com/maxgalbu [@mcuelenaere]: https://github.com/mcuelenaere [@mhujer]: https://github.com/mhujer