diff --git a/src/Ruleset.php b/src/Ruleset.php
index b6279bf9f3..81774a6b7a 100644
--- a/src/Ruleset.php
+++ b/src/Ruleset.php
@@ -1457,7 +1457,9 @@ public function populateTokenListeners()
$sniffCode = Common::getSniffCode($sniffClass);
$this->sniffCodes[$sniffCode] = $sniffClass;
+ $isDeprecated = false;
if ($this->sniffs[$sniffClass] instanceof DeprecatedSniff) {
+ $isDeprecated = true;
$this->deprecatedSniffs[$sniffCode] = $sniffClass;
}
@@ -1470,6 +1472,24 @@ public function populateTokenListeners()
$tokenizers = [];
$vars = get_class_vars($sniffClass);
+ if (empty($vars['supportedTokenizers']) === false
+ && $isDeprecated === false
+ && in_array('PHP', $vars['supportedTokenizers'], true) === false
+ ) {
+ if (in_array('CSS', $vars['supportedTokenizers'], true) === true
+ || in_array('JS', $vars['supportedTokenizers'], true) === true
+ ) {
+ $message = 'Scanning CSS/JS files is deprecated and support will be removed in PHP_CodeSniffer 4.0.'.PHP_EOL;
+ } else {
+ // Just in case someone has an integration with a custom tokenizer.
+ $message = 'Support for custom tokenizers will be removed in PHP_CodeSniffer 4.0.'.PHP_EOL;
+ }
+
+ $message .= 'The %s sniff is listening for %s.';
+ $message = sprintf($message, $sniffCode, implode(', ', $vars['supportedTokenizers']));
+ $this->msgCache->add($message, MessageCollector::DEPRECATED);
+ }
+
if (isset($vars['supportedTokenizers']) === true) {
foreach ($vars['supportedTokenizers'] as $tokenizer) {
$tokenizers[$tokenizer] = $tokenizer;
diff --git a/tests/Core/Filters/Filter/AcceptTest.xml b/tests/Core/Filters/Filter/AcceptTest.xml
index 3d3e1de080..2800298ebf 100644
--- a/tests/Core/Filters/Filter/AcceptTest.xml
+++ b/tests/Core/Filters/Filter/AcceptTest.xml
@@ -8,6 +8,8 @@
*/Other/Main\.php$
+
+
/anything/*
diff --git a/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/SupportedTokenizers/ImplementsDeprecatedInterfaceSniff.php b/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/SupportedTokenizers/ImplementsDeprecatedInterfaceSniff.php
new file mode 100644
index 0000000000..af095072bd
--- /dev/null
+++ b/tests/Core/Ruleset/Fixtures/TestStandard/Sniffs/SupportedTokenizers/ImplementsDeprecatedInterfaceSniff.php
@@ -0,0 +1,46 @@
+
+ * @copyright 2025 PHPCSStandards and contributors
+ * @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
+ */
+
+namespace PHP_CodeSniffer\Tests\Core\Ruleset;
+
+use PHP_CodeSniffer\Ruleset;
+use PHP_CodeSniffer\Tests\ConfigDouble;
+use PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase;
+
+/**
+ * Test the Ruleset::populateTokenListeners() method shows a deprecation notice for sniffs supporting JS and/or CSS tokenizers.
+ *
+ * @covers \PHP_CodeSniffer\Ruleset::populateTokenListeners
+ */
+final class PopulateTokenListenersSupportedTokenizersTest extends AbstractRulesetTestCase
+{
+
+ /**
+ * The Config object.
+ *
+ * @var \PHP_CodeSniffer\Config
+ */
+ private static $config;
+
+
+ /**
+ * Initialize the config and ruleset objects for this test.
+ *
+ * @beforeClass
+ *
+ * @return void
+ */
+ public static function initializeConfig()
+ {
+ // Set up the ruleset.
+ $standard = __DIR__.'/PopulateTokenListenersSupportedTokenizersTest.xml';
+ self::$config = new ConfigDouble(["--standard=$standard"]);
+
+ }//end initializeConfig()
+
+
+ /**
+ * Verify that a deprecation notice is shown if a non-deprecated sniff supports the JS/CSS tokenizer(s).
+ *
+ * Additionally, this test verifies that:
+ * - No deprecation notice is thrown if the complete sniff is deprecated.
+ * - No deprecation notice is thrown when the sniff _also_ supports PHP.
+ * - No deprecation notice is thrown when no tokenizers are supported (not sure why anyone would do that, but :shrug:).
+ *
+ * {@internal The test uses a data provider to verify the messages as the _order_ of the messages depends
+ * on the OS on which the tests are run (order in which files are retrieved), which makes the order within the
+ * complete message too unpredictable to test in one go.}
+ *
+ * @param string $expected The expected message output in regex format.
+ *
+ * @dataProvider dataDeprecatedTokenizersTriggerDeprecationNotice
+ *
+ * @return void
+ */
+ public function testDeprecatedTokenizersTriggerDeprecationNotice($expected)
+ {
+ $this->expectOutputRegex($expected);
+
+ new Ruleset(self::$config);
+
+ }//end testDeprecatedTokenizersTriggerDeprecationNotice()
+
+
+ /**
+ * Data provider.
+ *
+ * @see testDeprecatedTokenizersTriggerDeprecationNotice()
+ *
+ * @return array>
+ */
+ public static function dataDeprecatedTokenizersTriggerDeprecationNotice()
+ {
+ $cssJsDeprecated = '`DEPRECATED: Scanning CSS/JS files is deprecated and support will be removed in PHP_CodeSniffer 4\.0\.\R';
+ $cssJsDeprecated .= 'The %1$s sniff is listening for %2$s\.\R`';
+
+ $customTokenizer = '`DEPRECATED: Support for custom tokenizers will be removed in PHP_CodeSniffer 4\.0\.\R';
+ $customTokenizer .= 'The %1$s sniff is listening for %2$s\.\R`';
+
+ return [
+ 'Listens for CSS' => [
+ 'expected' => sprintf($cssJsDeprecated, 'TestStandard.SupportedTokenizers.ListensForCSS', 'CSS'),
+ ],
+ 'Listens for JS' => [
+ 'expected' => sprintf($cssJsDeprecated, 'TestStandard.SupportedTokenizers.ListensForJS', 'JS'),
+ ],
+ 'Listens for both CSS and JS' => [
+ 'expected' => sprintf($cssJsDeprecated, 'TestStandard.SupportedTokenizers.ListensForCSSAndJS', 'CSS, JS'),
+ ],
+ 'Listens for CSS and something unrecognized' => [
+ 'expected' => sprintf($cssJsDeprecated, 'TestStandard.SupportedTokenizers.ListensForCSSAndUnrecognized', 'CSS, Unrecognized'),
+ ],
+ 'Listens for only unrecognized tokenizers' => [
+ 'expected' => sprintf($customTokenizer, 'TestStandard.SupportedTokenizers.ListensForUnrecognizedTokenizers', 'SCSS, TypeScript'),
+ ],
+ ];
+
+ }//end dataDeprecatedTokenizersTriggerDeprecationNotice()
+
+
+}//end class
diff --git a/tests/Core/Ruleset/PopulateTokenListenersSupportedTokenizersTest.xml b/tests/Core/Ruleset/PopulateTokenListenersSupportedTokenizersTest.xml
new file mode 100644
index 0000000000..1a02eba182
--- /dev/null
+++ b/tests/Core/Ruleset/PopulateTokenListenersSupportedTokenizersTest.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/tests/Core/Ruleset/PopulateTokenListenersTest.php b/tests/Core/Ruleset/PopulateTokenListenersTest.php
index 6f79c10da1..347ffddee7 100644
--- a/tests/Core/Ruleset/PopulateTokenListenersTest.php
+++ b/tests/Core/Ruleset/PopulateTokenListenersTest.php
@@ -131,31 +131,31 @@ public function testRegistersSniffsToListenToTokens($sniffClass, $expectedCount)
public static function dataSniffListensToTokenss()
{
return [
- 'Generic.Files.EndFileNewline' => [
- 'sniffClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\Files\\EndFileNewlineSniff',
+ 'TestStandard.SupportedTokenizers.ListensForPHPAndCSSAndJS' => [
+ 'sniffClass' => 'Fixtures\\TestStandard\\Sniffs\\SupportedTokenizers\\ListensForPHPAndCSSAndJSSniff',
'expectedCount' => 2,
],
- 'Generic.NamingConventions.UpperCaseConstantName' => [
+ 'Generic.NamingConventions.UpperCaseConstantName' => [
'sniffClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\NamingConventions\\UpperCaseConstantNameSniff',
'expectedCount' => 2,
],
- 'PSR1.Files.SideEffects' => [
+ 'PSR1.Files.SideEffects' => [
'sniffClass' => 'PHP_CodeSniffer\\Standards\\PSR1\\Sniffs\\Files\\SideEffectsSniff',
'expectedCount' => 1,
],
- 'PSR12.ControlStructures.BooleanOperatorPlacement' => [
+ 'PSR12.ControlStructures.BooleanOperatorPlacement' => [
'sniffClass' => 'PHP_CodeSniffer\\Standards\\PSR12\\Sniffs\\ControlStructures\\BooleanOperatorPlacementSniff',
'expectedCount' => 5,
],
- 'Squiz.ControlStructures.ForEachLoopDeclaration' => [
+ 'Squiz.ControlStructures.ForEachLoopDeclaration' => [
'sniffClass' => 'PHP_CodeSniffer\\Standards\\Squiz\\Sniffs\\ControlStructures\\ForEachLoopDeclarationSniff',
'expectedCount' => 1,
],
- 'TestStandard.Deprecated.WithReplacement' => [
+ 'TestStandard.Deprecated.WithReplacement' => [
'sniffClass' => 'Fixtures\\TestStandard\\Sniffs\\Deprecated\\WithReplacementSniff',
'expectedCount' => 1,
],
- 'TestStandard.ValidSniffs.RegisterEmptyArray' => [
+ 'TestStandard.ValidSniffs.RegisterEmptyArray' => [
'sniffClass' => 'Fixtures\\TestStandard\\Sniffs\\ValidSniffs\\RegisterEmptyArraySniff',
'expectedCount' => 0,
],
@@ -313,7 +313,7 @@ public function testSetsClassAndSourceIndexes()
*/
public function testSetsSupportedTokenizersToPHPByDefault()
{
- $exclude = 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\Files\\EndFileNewlineSniff';
+ $exclude = 'Fixtures\\TestStandard\\Sniffs\\SupportedTokenizers\\ListensForPHPAndCSSAndJSSniff';
$expected = ['PHP' => 'PHP'];
foreach (self::$ruleset->tokenListeners as $token => $listeners) {
@@ -354,7 +354,7 @@ public function testSetsSupportedTokenizersToPHPByDefault()
*/
public function testSetsSupportedTokenizersWhenProvidedBySniff($token)
{
- $sniffClass = 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\Files\\EndFileNewlineSniff';
+ $sniffClass = 'Fixtures\\TestStandard\\Sniffs\\SupportedTokenizers\\ListensForPHPAndCSSAndJSSniff';
$expected = [
'PHP' => 'PHP',
'JS' => 'JS',
diff --git a/tests/Core/Ruleset/PopulateTokenListenersTest.xml b/tests/Core/Ruleset/PopulateTokenListenersTest.xml
index f61ab500d0..2116048ad0 100644
--- a/tests/Core/Ruleset/PopulateTokenListenersTest.xml
+++ b/tests/Core/Ruleset/PopulateTokenListenersTest.xml
@@ -21,7 +21,7 @@
-
+
diff --git a/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml b/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml
index 3b8b4c9b41..4ca8458e95 100644
--- a/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml
+++ b/tests/Core/Ruleset/ProcessRulesetAutoExpandSniffsDirectoryTest.xml
@@ -4,6 +4,7 @@
+
diff --git a/tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml b/tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml
index 8ce97e2dd3..a7c05017d0 100644
--- a/tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml
+++ b/tests/Core/Ruleset/SetPropertyDoesNotThrowErrorOnInvalidPropertyWhenSetForStandardTest.xml
@@ -2,6 +2,7 @@
+
diff --git a/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml b/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
index ab632b1959..8dee19d8e1 100644
--- a/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
+++ b/tests/Core/Ruleset/ShowSniffDeprecationsTest.xml
@@ -5,6 +5,7 @@
+