Skip to content

Commit 3c36ab5

Browse files
authored
Merge pull request #987 from PHPCSStandards/phpcs-4.0/feature/689-remove-support-sniffs-breaking-naming-conventions
Ruleset: remove support for sniffs not following the naming conventions
2 parents f267f46 + c2636f5 commit 3c36ab5

File tree

5 files changed

+67
-114
lines changed

5 files changed

+67
-114
lines changed

src/Ruleset.php

+11-14
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace PHP_CodeSniffer;
1313

14+
use InvalidArgumentException;
1415
use PHP_CodeSniffer\Exceptions\RuntimeException;
1516
use PHP_CodeSniffer\Sniffs\DeprecatedSniff;
1617
use PHP_CodeSniffer\Util\Common;
@@ -1446,23 +1447,19 @@ public function populateTokenListeners()
14461447
$this->tokenListeners = [];
14471448

14481449
foreach ($this->sniffs as $sniffClass => $sniffObject) {
1449-
$this->sniffs[$sniffClass] = null;
1450-
$this->sniffs[$sniffClass] = new $sniffClass();
1451-
1452-
$sniffCode = Common::getSniffCode($sniffClass);
1453-
1454-
if (substr($sniffCode, 0, 1) === '.'
1455-
|| substr($sniffCode, -1) === '.'
1456-
|| strpos($sniffCode, '..') !== false
1457-
|| preg_match('`(^|\.)Sniffs\.`', $sniffCode) === 1
1458-
|| preg_match('`[^\s\.-]+\\\\Sniffs\\\\[^\s\.-]+\\\\[^\s\.-]+Sniff`', $sniffClass) !== 1
1459-
) {
1460-
$message = "The sniff $sniffClass does not comply with the PHP_CodeSniffer naming conventions.";
1461-
$message .= ' This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
1450+
try {
1451+
$sniffCode = Common::getSniffCode($sniffClass);
1452+
} catch (InvalidArgumentException $e) {
1453+
$message = "The sniff $sniffClass does not comply with the PHP_CodeSniffer naming conventions.".PHP_EOL;
14621454
$message .= 'Contact the sniff author to fix the sniff.';
1463-
$this->msgCache->add($message, MessageCollector::DEPRECATED);
1455+
$this->msgCache->add($message, MessageCollector::ERROR);
1456+
1457+
// Unregister the sniff.
1458+
unset($this->sniffs[$sniffClass]);
1459+
continue;
14641460
}
14651461

1462+
$this->sniffs[$sniffClass] = new $sniffClass();
14661463
$this->sniffCodes[$sniffCode] = $sniffClass;
14671464

14681465
$isDeprecated = false;

src/Runner.php

-1
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,6 @@ public function processFile($file)
643643
try {
644644
if (empty($nextStack) === false
645645
&& isset($nextStack['class']) === true
646-
&& substr($nextStack['class'], -5) === 'Sniff'
647646
) {
648647
$sniffCode = 'the '.Common::getSniffCode($nextStack['class']).' sniff';
649648
}

src/Util/Common.php

+16-14
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ public static function suggestType($varType)
532532
* @return string
533533
*
534534
* @throws \InvalidArgumentException When $sniffClass is not a non-empty string.
535-
* @throws \InvalidArgumentException When $sniffClass is not a FQN for a sniff(test) class.
535+
* @throws \InvalidArgumentException When $sniffClass is not a valid FQN for a sniff(test) class.
536536
*/
537537
public static function getSniffCode($sniffClass)
538538
{
@@ -542,12 +542,22 @@ public static function getSniffCode($sniffClass)
542542

543543
$parts = explode('\\', $sniffClass);
544544
$partsCount = count($parts);
545-
$sniff = $parts[($partsCount - 1)];
545+
if ($partsCount < 4
546+
|| ($parts[($partsCount - 3)] !== 'Sniffs'
547+
&& $parts[($partsCount - 3)] !== 'Tests')
548+
|| $parts[($partsCount - 2)] === 'Sniffs'
549+
) {
550+
throw new InvalidArgumentException(
551+
'The $sniffClass parameter was not passed a fully qualified sniff(test) class name. Received: '.$sniffClass
552+
);
553+
}
554+
555+
$sniff = $parts[($partsCount - 1)];
546556

547-
if (substr($sniff, -5) === 'Sniff') {
557+
if ($sniff !== 'Sniff' && substr($sniff, -5) === 'Sniff') {
548558
// Sniff class name.
549559
$sniff = substr($sniff, 0, -5);
550-
} else if (substr($sniff, -8) === 'UnitTest') {
560+
} else if ($sniff !== 'UnitTest' && substr($sniff, -8) === 'UnitTest') {
551561
// Unit test class name.
552562
$sniff = substr($sniff, 0, -8);
553563
} else {
@@ -556,16 +566,8 @@ public static function getSniffCode($sniffClass)
556566
);
557567
}
558568

559-
$standard = '';
560-
if (isset($parts[($partsCount - 4)]) === true) {
561-
$standard = $parts[($partsCount - 4)];
562-
}
563-
564-
$category = '';
565-
if (isset($parts[($partsCount - 2)]) === true) {
566-
$category = $parts[($partsCount - 2)];
567-
}
568-
569+
$standard = $parts[($partsCount - 4)];
570+
$category = $parts[($partsCount - 2)];
569571
return $standard.'.'.$category.'.'.$sniff;
570572

571573
}//end getSniffCode()

tests/Core/Ruleset/PopulateTokenListenersNamingConventionsTest.php

+25-37
Original file line numberDiff line numberDiff line change
@@ -11,70 +11,58 @@
1111

1212
use PHP_CodeSniffer\Ruleset;
1313
use PHP_CodeSniffer\Tests\ConfigDouble;
14-
use PHPUnit\Framework\TestCase;
14+
use PHP_CodeSniffer\Tests\Core\Ruleset\AbstractRulesetTestCase;
1515

1616
/**
1717
* Test handling of sniffs not following the PHPCS naming conventions in the Ruleset::populateTokenListeners() method.
1818
*
1919
* @covers \PHP_CodeSniffer\Ruleset::populateTokenListeners
2020
*/
21-
final class PopulateTokenListenersNamingConventionsTest extends TestCase
21+
final class PopulateTokenListenersNamingConventionsTest extends AbstractRulesetTestCase
2222
{
2323

2424

2525
/**
2626
* Verify a warning is shown for sniffs not complying with the PHPCS naming conventions.
2727
*
2828
* Including sniffs which do not comply with the PHPCS naming conventions is soft deprecated since
29-
* PHPCS 3.12.0, hard deprecated since PHPCS 3.13.0 and support will be removed in PHPCS 4.0.0.
29+
* PHPCS 3.12.0, hard deprecated since PHPCS 3.13.0 and support has been removed in PHPCS 4.0.0.
3030
*
3131
* @return void
3232
*/
3333
public function testBrokenNamingConventions()
3434
{
35+
$expectedMessage = 'ERROR: The sniff BrokenNamingConventions\\Sniffs\\MissingCategoryDirSniff does not comply';
36+
$expectedMessage .= ' with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
37+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
38+
$expectedMessage .= 'ERROR: The sniff NoNamespaceSniff does not comply with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
39+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
40+
$expectedMessage .= 'ERROR: The sniff Sniffs\PartialNamespaceSniff does not comply with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
41+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
42+
$expectedMessage .= 'ERROR: The sniff BrokenNamingConventions\Sniffs\Category\Sniff does not comply';
43+
$expectedMessage .= ' with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
44+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
45+
$expectedMessage .= 'ERROR: The sniff BrokenNamingConventions\Sniffs\Sniffs\CategoryCalledSniffsSniff does not';
46+
$expectedMessage .= ' comply with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
47+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
48+
$expectedMessage .= 'ERROR: The sniff BrokenNamingConventions\Sniffs\Category\SubDir\TooDeeplyNestedSniff';
49+
$expectedMessage .= ' does not comply with the PHP_CodeSniffer naming conventions.'.PHP_EOL;
50+
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL.PHP_EOL;
51+
52+
$this->expectRuntimeExceptionMessage($expectedMessage);
53+
3554
// Set up the ruleset.
3655
$standard = __DIR__.'/PopulateTokenListenersNamingConventionsTest.xml';
3756
$config = new ConfigDouble(["--standard=$standard"]);
3857
$ruleset = new Ruleset($config);
3958

4059
// The "Generic.PHP.BacktickOperator" sniff is the only valid sniff.
4160
$expectedSniffCodes = [
42-
'..NoNamespace' => 'NoNamespaceSniff',
43-
'.Sniffs.MissingCategoryDir' => 'BrokenNamingConventions\\Sniffs\\MissingCategoryDirSniff',
44-
'.Sniffs.PartialNamespace' => 'Sniffs\\PartialNamespaceSniff',
45-
'BrokenNamingConventions.Category.' => 'BrokenNamingConventions\\Sniffs\\Category\\Sniff',
46-
'BrokenNamingConventions.Sniffs.CategoryCalledSniffs' => 'BrokenNamingConventions\\Sniffs\\Sniffs\\CategoryCalledSniffsSniff',
47-
'Generic.PHP.BacktickOperator' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\BacktickOperatorSniff',
48-
'Sniffs.SubDir.TooDeeplyNested' => 'BrokenNamingConventions\\Sniffs\\Category\\SubDir\\TooDeeplyNestedSniff',
61+
'Generic.PHP.BacktickOperator' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\PHP\\BacktickOperatorSniff',
4962
];
5063

51-
// Sort the value to make the tests stable as different OSes will read directories
52-
// in a different order and the order is not relevant for these tests. Just the values.
53-
$actual = $ruleset->sniffCodes;
54-
ksort($actual);
55-
56-
$this->assertSame($expectedSniffCodes, $actual, 'Registered sniffs do not match expectation');
57-
58-
$expectedMessage = 'DEPRECATED: The sniff BrokenNamingConventions\\Sniffs\\MissingCategoryDirSniff does not comply';
59-
$expectedMessage .= ' with the PHP_CodeSniffer naming conventions. This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
60-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
61-
$expectedMessage .= 'DEPRECATED: The sniff NoNamespaceSniff does not comply with the PHP_CodeSniffer naming conventions.';
62-
$expectedMessage .= ' This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
63-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
64-
$expectedMessage .= 'DEPRECATED: The sniff Sniffs\\PartialNamespaceSniff does not comply with the PHP_CodeSniffer naming conventions.';
65-
$expectedMessage .= ' This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
66-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
67-
$expectedMessage .= 'DEPRECATED: The sniff BrokenNamingConventions\\Sniffs\\Category\\Sniff does not comply';
68-
$expectedMessage .= ' with the PHP_CodeSniffer naming conventions. This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
69-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
70-
$expectedMessage .= 'DEPRECATED: The sniff BrokenNamingConventions\\Sniffs\\Sniffs\\CategoryCalledSniffsSniff does not';
71-
$expectedMessage .= ' comply with the PHP_CodeSniffer naming conventions. This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
72-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL;
73-
$expectedMessage .= 'DEPRECATED: The sniff BrokenNamingConventions\\Sniffs\\Category\\SubDir\\TooDeeplyNestedSniff';
74-
$expectedMessage .= ' does not comply with the PHP_CodeSniffer naming conventions. This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
75-
$expectedMessage .= 'Contact the sniff author to fix the sniff.'.PHP_EOL.PHP_EOL;
76-
77-
$this->expectOutputString($expectedMessage);
64+
// This assertion will only take effect for PHPUnit 10+.
65+
$this->assertSame($expectedSniffCodes, $ruleset->sniffCodes, 'Registered sniffs do not match expectation');
7866

7967
}//end testBrokenNamingConventions()
8068

tests/Core/Util/Common/GetSniffCodeTest.php

+15-48
Original file line numberDiff line numberDiff line change
@@ -105,9 +105,16 @@ public function testGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass(
105105
public static function dataGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
106106
{
107107
return [
108-
'Unqualified class name' => ['ClassName'],
109-
'Fully qualified class name, not enough parts' => ['Fully\\Qualified\\ClassName'],
108+
'Unqualified class name' => ['ClassNameSniff'],
109+
'Fully qualified sniff class name, not enough parts [1]' => ['Fully\\Qualified\\ClassNameSniff'],
110+
'Fully qualified sniff class name, not enough parts [2]' => ['CompanyName\\CheckMeSniff'],
111+
'Fully qualified test class name, not enough parts' => ['Fully\\Qualified\\ClassNameUnitTest'],
110112
'Fully qualified class name, doesn\'t end on Sniff or UnitTest' => ['Fully\\Sniffs\\Qualified\\ClassName'],
113+
'Fully qualified class name, ends on Sniff, but isn\'t' => ['Fully\\Sniffs\\AbstractSomethingSniff'],
114+
'Fully qualified class name, last part *is* Sniff' => ['CompanyName\\Sniffs\\Category\\Sniff'],
115+
'Fully qualified class name, last part *is* UnitTest' => ['CompanyName\\Tests\\Category\\UnitTest'],
116+
'Fully qualified class name, no Sniffs or Tests leaf' => ['CompanyName\\CustomSniffs\\Whatever\\CheckMeSniff'],
117+
'Fully qualified class name, category called Sniffs' => ['CompanyName\\Sniffs\\Sniffs\\InvalidCategorySniff'],
111118
];
112119

113120
}//end dataGetSniffCodeThrowsExceptionOnInputWhichIsNotASniffTestClass()
@@ -140,70 +147,30 @@ public function testGetSniffCode($fqnClass, $expected)
140147
public static function dataGetSniffCode()
141148
{
142149
return [
143-
'PHPCS native sniff' => [
150+
'PHPCS native sniff' => [
144151
'fqnClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Sniffs\\Arrays\\ArrayIndentSniff',
145152
'expected' => 'Generic.Arrays.ArrayIndent',
146153
],
147-
'Class is a PHPCS native test class' => [
154+
'Class is a PHPCS native test class' => [
148155
'fqnClass' => 'PHP_CodeSniffer\\Standards\\Generic\\Tests\\Arrays\\ArrayIndentUnitTest',
149156
'expected' => 'Generic.Arrays.ArrayIndent',
150157
],
151-
'Sniff in external standard without namespace prefix' => [
158+
'Sniff in external standard without namespace prefix' => [
152159
'fqnClass' => 'MyStandard\\Sniffs\\PHP\\MyNameSniff',
153160
'expected' => 'MyStandard.PHP.MyName',
154161
],
155-
'Test in external standard without namespace prefix' => [
162+
'Test in external standard without namespace prefix' => [
156163
'fqnClass' => 'MyStandard\\Tests\\PHP\\MyNameUnitTest',
157164
'expected' => 'MyStandard.PHP.MyName',
158165
],
159-
'Sniff in external standard with namespace prefix' => [
166+
'Sniff in external standard with namespace prefix' => [
160167
'fqnClass' => 'Vendor\\Package\\MyStandard\\Sniffs\\Category\\AnalyzeMeSniff',
161168
'expected' => 'MyStandard.Category.AnalyzeMe',
162169
],
163-
'Test in external standard with namespace prefix' => [
170+
'Test in external standard with namespace prefix' => [
164171
'fqnClass' => 'Vendor\\Package\\MyStandard\\Tests\\Category\\AnalyzeMeUnitTest',
165172
'expected' => 'MyStandard.Category.AnalyzeMe',
166173
],
167-
168-
/*
169-
* These are not valid sniff codes and is an undesirable result, but can't be helped
170-
* as changing this would be a BC-break.
171-
* Supporting these to allow for <rule> tags directly including sniff files.
172-
* See: https://github.com/PHPCSStandards/PHP_CodeSniffer/issues/675
173-
*/
174-
175-
'Fully qualified class name, ends on Sniff, but isn\'t' => [
176-
'fqnClass' => 'Fully\\Sniffs\\AbstractSomethingSniff',
177-
'expected' => '.Sniffs.AbstractSomething',
178-
],
179-
'Sniff provided via file include and doesn\'t comply with naming conventions [1]' => [
180-
'fqnClass' => 'CheckMeSniff',
181-
'expected' => '..CheckMe',
182-
],
183-
'Sniff provided via file include and doesn\'t comply with naming conventions [2]' => [
184-
'fqnClass' => 'CompanyName\\CheckMeSniff',
185-
'expected' => '.CompanyName.CheckMe',
186-
],
187-
'Sniff provided via file include and doesn\'t comply with naming conventions [3]' => [
188-
'fqnClass' => 'CompanyName\\Sniffs\\CheckMeSniff',
189-
'expected' => '.Sniffs.CheckMe',
190-
],
191-
'Sniff provided via file include and doesn\'t comply with naming conventions [4]' => [
192-
'fqnClass' => 'CompanyName\\CustomSniffs\\Whatever\\CheckMeSniff',
193-
'expected' => 'CompanyName.Whatever.CheckMe',
194-
],
195-
'Sniff provided via file include and doesn\'t comply with naming conventions [5]' => [
196-
'fqnClass' => 'CompanyName\\Sniffs\\Category\\Sniff',
197-
'expected' => 'CompanyName.Category.',
198-
],
199-
'Sniff provided via file include and doesn\'t comply with naming conventions [6]' => [
200-
'fqnClass' => 'CompanyName\\Tests\\Category\\UnitTest',
201-
'expected' => 'CompanyName.Category.',
202-
],
203-
'Sniff provided via file include and doesn\'t comply with naming conventions [7]' => [
204-
'fqnClass' => 'Sniffs\\Category\\NamedSniff',
205-
'expected' => '.Category.Named',
206-
],
207174
];
208175

209176
}//end dataGetSniffCode()

0 commit comments

Comments
 (0)