Skip to content

Commit 44dd259

Browse files
authored
Merge pull request #892 from PHPCSStandards/feature/689-ruleset-hard-deprecate-sniffs-breaking-naming-conventions
Ruleset: hard deprecate support for sniffs not following the naming conventions
2 parents e207df2 + d23fa30 commit 44dd259

12 files changed

+263
-37
lines changed

src/Ruleset.php

+13
Original file line numberDiff line numberDiff line change
@@ -1467,6 +1467,19 @@ public function populateTokenListeners()
14671467
$this->sniffs[$sniffClass] = new $sniffClass();
14681468

14691469
$sniffCode = Common::getSniffCode($sniffClass);
1470+
1471+
if (substr($sniffCode, 0, 1) === '.'
1472+
|| substr($sniffCode, -1) === '.'
1473+
|| strpos($sniffCode, '..') !== false
1474+
|| preg_match('`(^|\.)Sniffs\.`', $sniffCode) === 1
1475+
|| preg_match('`[^\s\.-]+\\\\Sniffs\\\\[^\s\.-]+\\\\[^\s\.-]+Sniff`', $sniffClass) !== 1
1476+
) {
1477+
$message = "The sniff $sniffClass does not comply with the PHP_CodeSniffer naming conventions.";
1478+
$message .= ' This will no longer be supported in PHPCS 4.0.'.PHP_EOL;
1479+
$message .= 'Contact the sniff author to fix the sniff.';
1480+
$this->msgCache->add($message, MessageCollector::DEPRECATED);
1481+
}
1482+
14701483
$this->sniffCodes[$sniffCode] = $sniffClass;
14711484

14721485
if ($this->sniffs[$sniffClass] instanceof DeprecatedSniff) {

tests/Core/Ruleset/ExpandSniffDirectoryTest.php

+2-13
Original file line numberDiff line numberDiff line change
@@ -49,20 +49,9 @@ public function testExpandSniffDirectory()
4949
$this->assertNotFalse($expectedPathToRuleset, 'Ruleset file could not be found');
5050
$this->assertContains($expectedPathToRuleset, $ruleset->paths, 'Ruleset file not included in the "seen ruleset paths"');
5151

52-
/*
53-
* Take note: the expectation includes some "undesirables" related to the convoluted directory structure
54-
* in the "standard" used as a test fixture.
55-
*
56-
* That is okay as (for now) non-standard directory layouts are supported.
57-
*
58-
* This test is not about the standard directory layout.
59-
*/
60-
6152
$expectedSniffCodes = [
62-
'.Sniffs.IncorrectLevelShouldStillBeFound' => 'MyStandard\\Sniffs\\IncorrectLevelShouldStillBeFoundSniff',
63-
'MyStandard.CategoryA.FindMe' => 'MyStandard\\Sniffs\\CategoryA\\FindMeSniff',
64-
'MyStandard.CategoryB.FindMe' => 'MyStandard\\Sniffs\\CategoryB\\FindMeSniff',
65-
'Sniffs.SubDir.IncorrectLevelShouldStillBeFound' => 'MyStandard\\Sniffs\\CategoryA\\SubDir\\IncorrectLevelShouldStillBeFoundSniff',
53+
'MyStandard.CategoryA.FindMe' => 'MyStandard\\Sniffs\\CategoryA\\FindMeSniff',
54+
'MyStandard.CategoryB.FindMe' => 'MyStandard\\Sniffs\\CategoryB\\FindMeSniff',
6655
];
6756

6857
// Sort the value to make the tests stable as different OSes will read directories
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\RulesetPopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
namespace BrokenNamingConventions\Sniffs\Category;
9+
10+
use PHP_CodeSniffer\Files\File;
11+
use PHP_CodeSniffer\Sniffs\Sniff as PHPCS_Sniff;
12+
13+
final class Sniff implements PHPCS_Sniff
14+
{
15+
public function register()
16+
{
17+
return [T_OPEN_TAG];
18+
}
19+
20+
public function process(File $phpcsFile, $stackPtr)
21+
{
22+
// Do something.
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\RulesetPopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
namespace BrokenNamingConventions\Sniffs\Category\SubDir;
9+
10+
use PHP_CodeSniffer\Files\File;
11+
use PHP_CodeSniffer\Sniffs\Sniff;
12+
13+
final class TooDeeplyNestedSniff implements Sniff
14+
{
15+
public function register()
16+
{
17+
return [T_OPEN_TAG];
18+
}
19+
20+
public function process(File $phpcsFile, $stackPtr)
21+
{
22+
// Do something.
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\RulesetPopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
namespace BrokenNamingConventions\Sniffs;
9+
10+
use PHP_CodeSniffer\Files\File;
11+
use PHP_CodeSniffer\Sniffs\Sniff;
12+
13+
final class MissingCategoryDirSniff implements Sniff
14+
{
15+
public function register()
16+
{
17+
return [T_OPEN_TAG];
18+
}
19+
20+
public function process(File $phpcsFile, $stackPtr)
21+
{
22+
// Do something.
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\Ruleset\PopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
use PHP_CodeSniffer\Files\File;
9+
use PHP_CodeSniffer\Sniffs\Sniff;
10+
11+
final class NoNamespaceSniff implements Sniff
12+
{
13+
public function register()
14+
{
15+
return [T_OPEN_TAG];
16+
}
17+
18+
public function process(File $phpcsFile, $stackPtr)
19+
{
20+
// Do something.
21+
}
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\Ruleset\PopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
namespace Sniffs;
9+
10+
use PHP_CodeSniffer\Files\File;
11+
use PHP_CodeSniffer\Sniffs\Sniff;
12+
13+
final class PartialNamespaceSniff implements Sniff
14+
{
15+
public function register()
16+
{
17+
return [T_OPEN_TAG];
18+
}
19+
20+
public function process(File $phpcsFile, $stackPtr)
21+
{
22+
// Do something.
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
/**
3+
* Test fixture.
4+
*
5+
* @see \PHP_CodeSniffer\Tests\Core\RulesetPopulateTokenListenersNamingConventionsTest
6+
*/
7+
8+
namespace BrokenNamingConventions\Sniffs\Sniffs;
9+
10+
use PHP_CodeSniffer\Files\File;
11+
use PHP_CodeSniffer\Sniffs\Sniff;
12+
13+
final class CategoryCalledSniffsSniff implements Sniff
14+
{
15+
public function register()
16+
{
17+
return [T_OPEN_TAG];
18+
}
19+
20+
public function process(File $phpcsFile, $stackPtr)
21+
{
22+
// Do something.
23+
}
24+
}

tests/Core/Ruleset/Fixtures/DirectoryExpansion/.hiddenAbove/src/MyStandard/Sniffs/CategoryA/Subdir/IncorrectLevelShouldStillBeFoundSniff.php

-12
This file was deleted.

tests/Core/Ruleset/Fixtures/DirectoryExpansion/.hiddenAbove/src/MyStandard/Sniffs/IncorrectLevelShouldStillBeFoundSniff.php

-12
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
/**
3+
* Test the Ruleset::expandSniffDirectory() method.
4+
*
5+
* @author Juliette Reinders Folmer <[email protected]>
6+
* @copyright 2024 PHPCSStandards and contributors
7+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\Ruleset;
11+
12+
use PHP_CodeSniffer\Ruleset;
13+
use PHP_CodeSniffer\Tests\ConfigDouble;
14+
use PHPUnit\Framework\TestCase;
15+
16+
/**
17+
* Test handling of sniffs not following the PHPCS naming conventions in the Ruleset::populateTokenListeners() method.
18+
*
19+
* @covers \PHP_CodeSniffer\Ruleset::populateTokenListeners
20+
*/
21+
final class PopulateTokenListenersNamingConventionsTest extends TestCase
22+
{
23+
24+
25+
/**
26+
* Verify a warning is shown for sniffs not complying with the PHPCS naming conventions.
27+
*
28+
* 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.
30+
*
31+
* @return void
32+
*/
33+
public function testBrokenNamingConventions()
34+
{
35+
// Set up the ruleset.
36+
$standard = __DIR__.'/PopulateTokenListenersNamingConventionsTest.xml';
37+
$config = new ConfigDouble(["--standard=$standard"]);
38+
$ruleset = new Ruleset($config);
39+
40+
// The "Generic.PHP.BacktickOperator" sniff is the only valid sniff.
41+
$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',
49+
];
50+
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);
78+
79+
}//end testBrokenNamingConventions()
80+
81+
82+
}//end class
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0"?>
2+
<ruleset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" name="PopulateTokenListenersNamingConventionsTest" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/PHPCSStandards/PHP_CodeSniffer/master/phpcs.xsd">
3+
4+
<!--
5+
These sniff files are not in an installed standard, there is not even a ruleset.xml file for the "standard".
6+
Each of these sniff files breaks one of the naming convention rules.
7+
-->
8+
9+
<!-- Breaks: All sniffs MUST be located within a [CategoryName] directory. -->
10+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/MissingCategoryDirSniff.php"/>
11+
<!-- Breaks: The namespace and class name MUST follow PSR-4. -->
12+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/NoNamespaceSniff.php"/>
13+
<!-- Breaks: The namespace and class name MUST follow PSR-4. -->
14+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/PartialNamespaceSniff.php"/>
15+
<!-- Breaks: All sniffs MUST have a name, so a sniff class called just and only Sniff is not allowed. -->
16+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/Category/Sniff.php"/>
17+
<!-- Breaks: The name "Sniffs" MUST NOT be used as a category name. -->
18+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/Sniffs/CategoryCalledSniffsSniff.php"/>
19+
<!-- Breaks: No directories should exist under the [CategoryName] directory. -->
20+
<rule ref="./Fixtures/BrokenNamingConventions/Sniffs/Category/SubDir/TooDeeplyNestedSniff.php"/>
21+
22+
<!-- Prevent a "no sniffs were registered" error (once the above become errors). -->
23+
<rule ref="Generic.PHP.BacktickOperator"/>
24+
</ruleset>

0 commit comments

Comments
 (0)