NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
Packagist · #4951 most downloaded on Packagist
DrevOps PHP_CodeSniffer rules: enforce consistent naming (snakeCase or camelCase) for variables and parameters, PHPUnit data provider conventions.
Last release 5 days ago
03 Oct 2026
Release timing varies
gaps range from 2 weeks to 7 months
Nearly every release is documented
notes for 11 of 11 stable releases
Nothing withdrawn
no release was ever pulled
11 months old
11 releases · first in 2025
One column per month.
[ #30 ] 'ParameterNaming' no longer skips parameters in classes whose ancestors live in Drupal modules, profiles and themes. @AlexSkrypnyk ( #32 ) Dru
[#30] 'ParameterNaming' no longer skips parameters in classes whose ancestors live in Drupal modules, profiles and themes. @AlexSkrypnyk (#32)
Drupal registers module, profile and theme namespaces at runtime, so Composer can't find their classes; ParameterNaming now reads them straight from the Drupal root, which it detects from the drupal/core install path. Their subclasses are now checked like any other class, so Drupal projects can see new ParameterNaming errors after upgrading: render(ResultRow $resultRow) in a FieldPluginBase subclass is reported because the parent names that parameter $values. Setting the new drupalRoot property to false turns discovery off and goes back to skipping those parameters, and setting it to a path covers a Drupal root that detection can't find.
[#29] Arrow function parameters are now reported by 'ParameterNaming' instead of 'LocalVariableNaming'. @AlexSkrypnyk (#31)
In 1.0.0 the sniffs didn't recognise fn($param) => ... parameters as parameters, so LocalVariableNaming reported the signature and every use in the body; now each parameter gets 1 ParameterNaming error on its signature, and closure and anonymous class method parameters stop getting a second LocalVariableNaming error in the body. A suppression that names LocalVariableNaming no longer silences arrow function parameters, so replace LocalVariableNaming with ParameterNaming in its code, as the migration notes below show. If you set different format values for the 2 sniffs, arrow function parameters now follow the ParameterNaming one.
[#22] 'ParameterNaming' now exempts only parameter names that an ancestor declares for the same method. @AlexSkrypnyk (#28)
1.0.0 decided from keywords alone: it skipped every parameter in interface and abstract method declarations and in any class with an extends or implements clause, so in codebases where most classes extend or implement something, the sniff was close to inert. It now looks up the extended class, interfaces and traits, so it checks renamed and extra parameters, private methods, and interface and abstract methods that declare a signature for the first time; on the vendored PHPCS, PHPUnit, Symfony and Slevomat sources, that took the sniff from 1469 violations to 2906. There's no switch to restore the old exemption, so rename or suppress the new errors; a method still has all its parameters skipped when an ancestor can't be found and none of the found ones declares it.
Dropped PHP 8.2 support and raised dev dependency floors to the latest PHP 8.3-compatible releases. @AlexSkrypnyk (#27)require.php is now >=8.3, so Composer won't install 1.1.0 on PHP 8.2, and projects there stay on 1.0.0 until they move to PHP 8.3. The runtime requirements don't change (squizlabs/php_codesniffer ^3.10 || ^4 and dealerdirect/phpcodesniffer-composer-installer ^1), and the raised floors only touch this package's own dev tooling. CI now covers PHP 8.3, 8.4 and 8.5 against PHPCS 3 and 4.
Only the sniff name changes: wherever a suppression covers an arrow function parameter, LocalVariableNaming becomes ParameterNaming. Ignores that name only DrevOps or DrevOps.NamingConventions cover both sniffs and keep working.
In an inline ignore, swap the sniff name:
-// phpcs:ignore DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase
+// phpcs:ignore DrevOps.NamingConventions.ParameterNaming.NotSnakeCase
$doubled = array_map(fn($itemValue) => $itemValue * 2, $items);The shorter DrevOps.NamingConventions.ParameterNaming works too, and with format="camelCase" the suffix is NotCamelCase, as before.
When the same line also has a camelCase local variable, 2 sniffs now report it, so list both codes, comma-separated:
-// phpcs:ignore DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase
+// phpcs:ignore DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase,DrevOps.NamingConventions.ParameterNaming.NotSnakeCase
$runningTotal = array_sum(array_map(fn($itemValue) => $itemValue * 2, $items));In phpcs:disable and phpcs:enable comments, list both sniffs the same way. A block that only covers arrow functions needs ParameterNaming alone.
In a ruleset, keep the LocalVariableNaming exclusion and add a matching one:
<rule ref="DrevOps.NamingConventions.LocalVariableNaming">
<exclude-pattern>*/src/Legacy/*</exclude-pattern>
</rule>
+<rule ref="DrevOps.NamingConventions.ParameterNaming">
+ <exclude-pattern>*/src/Legacy/*</exclude-pattern>
+</rule>No error code targets arrow function parameters alone, so this stops checking every parameter in those paths, method and closure parameters included. To skip only specific arrow functions, use an inline ignore.
uses: line in the repository's workflows now names a full commit SHA, with the release tag as a trailing comment, so a re-pointed upstream tag can't change what CI runs, and Renovate keeps the pins current. Test results now upload through codecov/codecov-action, because codecov/test-results-action is archived upstream. None of this changes the installed package.Full Changelog: 1.0.0...1.1.0
Update renovate config @AlexSkrypnyk
Full Changelog: 0.7.0...1.0.0
[ #17 ] Fixed false positives on variables prefixed with underscore. @AlexSkrypnyk
Full Changelog: 0.6.2...0.7.0
[ #15 ] Fixed false positives on variables prefixed with underscore. @AlexSkrypnyk
Full Changelog: 0.6.1...0.6.2
[ #13 ] Fixed nullable class properties incorrectly triggering LocalVariableNaming.NotSnakeCase . @AlexSkrypnyk
LocalVariableNaming.NotSnakeCase. @AlexSkrypnyk (#14)Full Changelog: 0.6.0...0.6.1
Allow to specify snake or camel case format for local variables and function parameters.
Allow to specify snake or camel case format for local variables and function parameters.
<rule ref="DrevOps.NamingConventions.LocalVariableNaming"/>
<rule ref="DrevOps.NamingConventions.ParameterNaming"/><rule ref="DrevOps.NamingConventions.LocalVariableNaming">
<properties>
<property name="format" value="camelCase"/>
</properties>
</rule>
<rule ref="DrevOps.NamingConventions.ParameterNaming">
<properties>
<property name="format" value="camelCase"/>
</properties>
</rule>- <rule ref="DrevOps.NamingConventions.LocalVariableSnakeCase"/>
+ <rule ref="DrevOps.NamingConventions.LocalVariableNaming"/>
- <rule ref="DrevOps.NamingConventions.ParameterSnakeCase"/>
+ <rule ref="DrevOps.NamingConventions.ParameterNaming"/>- // phpcs:ignore DrevOps.NamingConventions.LocalVariableSnakeCase.NotSnakeCase
+ // phpcs:ignore DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase
- // phpcs:ignore DrevOps.NamingConventions.ParameterSnakeCase.NotSnakeCase
+ // phpcs:ignore DrevOps.NamingConventions.ParameterNaming.NotSnakeCaseDrevOps rulesetFull Changelog: 0.5.0...0.6.0
Added 3 new checks. @AlexSkrypnyk
DrevOps.TestingPractices.DataProviderPrefix
DrevOps.TestingPractices.DataProviderMatchesTestName
DrevOps.TestingPractices.DataProviderOrder
Enforces naming conventions for PHPUnit data provider methods. This sniff ensures that data provider methods start with a configurable prefix (default: dataProvider). It provides auto-fixing capabilities to rename methods and update all references.
dataProvider)✓ Valid:
class UserTest extends TestCase {
/**
* @dataProvider dataProviderValidCases
*/
public function testSomething($input, $expected): void {
$this->assertEquals($expected, $input);
}
public function dataProviderValidCases(): array {
return [['input1', 'expected1']];
}
}✗ Invalid:
class UserTest extends TestCase {
/**
* @dataProvider providerInvalidCases
*/
public function testSomething($input, $expected): void {
$this->assertEquals($expected, $input);
}
// ERROR: Data provider method "providerInvalidCases" should start with
// prefix "dataProvider", suggested name: "dataProviderInvalidCases"
public function providerInvalidCases(): array {
return [['input1', 'expected1']];
}
/**
* @dataProvider casesForAnotherTest
*/
public function testAnother($value): void {
$this->assertNotEmpty($value);
}
// ERROR: Data provider method "casesForAnotherTest" should start with
// prefix "dataProvider", suggested name: "dataProviderForAnotherTest"
public function casesForAnotherTest(): array {
return [['value1']];
}
/**
* @dataProvider provideDataForTest
*/
public function testWithProvide($data): void {
$this->assertIsString($data);
}
// ERROR: Data provider method "provideDataForTest" should start with
// prefix "dataProvider", suggested name: "dataProviderDataForTest"
public function provideDataForTest(): array {
return [['data1']];
}
}Enforces that data provider method names match the test method names they serve. The provider name must end with the exact test name (after removing the "test" prefix). This improves code readability and maintains a clear relationship between tests and their data providers.
None
✓ Valid:
class UserTest extends TestCase {
/**
* @dataProvider dataProviderUserLogin
*/
public function testUserLogin($user, $pass): void {
$this->assertNotEmpty($user);
}
public function dataProviderUserLogin(): array {
return [['user1', 'pass1']];
}
/**
* @dataProvider providerEmailValidation
*/
public function testEmailValidation($email): void {
$this->assertNotEmpty($email);
}
// Different prefix is OK, as long as it ends with "EmailValidation"
public function providerEmailValidation(): array {
return [['test@example.com']];
}
/**
* @dataProvider AuthenticationScenarios
*/
public function testAuthenticationScenarios($scenario): void {
$this->assertIsString($scenario);
}
// No prefix is OK, as long as it matches exactly
public function AuthenticationScenarios(): array {
return [['scenario1']];
}
#[DataProvider('dataProviderPasswordValidation')]
public function testPasswordValidation($password): void {
$this->assertNotEmpty($password);
}
// Works with PHP 8 attributes too
public function dataProviderPasswordValidation(): array {
return [['password123']];
}
}✗ Invalid:
class UserTest extends TestCase {
/**
* @dataProvider dataProviderLogin
*/
public function testUserLogin($user, $pass): void {
$this->assertNotEmpty($user);
}
// ERROR: Data provider method "dataProviderLogin" does not match test
// method "testUserLogin". Expected provider name to end with "UserLogin"
public function dataProviderLogin(): array {
return [['user1', 'pass1']];
}
/**
* @dataProvider dataProviderEmailValidationCases
*/
public function testEmailValidation($email): void {
$this->assertNotEmpty($email);
}
// ERROR: Data provider method "dataProviderEmailValidationCases" does not
// match test method "testEmailValidation". Expected provider name to end
// with "EmailValidation"
public function dataProviderEmailValidationCases(): array {
return [['test@example.com']];
}
/**
* @dataProvider providerAuth
*/
public function testAuthenticationScenarios($scenario): void {
$this->assertIsString($scenario);
}
// ERROR: Data provider method "providerAuth" does not match test method
// "testAuthenticationScenarios". Expected provider name to end with
// "AuthenticationScenarios"
public function providerAuth(): array {
return [['scenario1']];
}
#[DataProvider('dataProviderPass')]
public function testPasswordValidation($password): void {
$this->assertNotEmpty($password);
}
// ERROR: Data provider method "dataProviderPass" does not match test
// method "testPasswordValidation". Expected provider name to end with
// "PasswordValidation"
public function dataProviderPass(): array {
return [['password123']];
}
}Enforces ordering of data provider and test methods in test files. This ensures structural organization and improves code readability. By default, data providers should be defined after their test methods. Helper methods between tests and providers are allowed.
after)
after: Data providers should appear after their test methods (default)before: Data providers should appear before their test methods✓ Valid (providerPosition="after"):
class UserTest extends TestCase {
/**
* @dataProvider dataProviderUserLogin
*/
public function testUserLogin($user, $pass): void {
$this->assertNotEmpty($user);
}
// Provider after test - CORRECT
public function dataProviderUserLogin(): array {
return [['user1', 'pass1']];
}
/**
* @dataProvider dataProviderEmailValidation
*/
public function testEmailValidation($email): void {
$this->assertNotEmpty($email);
}
// Helper methods between test and provider are allowed
private function helperValidate(): void {
// Helper logic
}
// Provider still after test - CORRECT
public function dataProviderEmailValidation(): array {
return [['test@example.com']];
}
#[DataProvider('dataProviderAuthentication')]
public function testAuthentication($scenario): void {
$this->assertIsString($scenario);
}
// Works with PHP 8 attributes too
public function dataProviderAuthentication(): array {
return [['scenario1']];
}
}✗ Invalid (providerPosition="after"):
class UserTest extends TestCase {
// Provider before test - WRONG ORDER
public function dataProviderUserLogin(): array {
return [['user1', 'pass1']];
}
/**
* @dataProvider dataProviderUserLogin
*/
// ERROR: Data provider method "dataProviderUserLogin" (line 10) appears
// before test method "testUserLogin" (line 17). Providers should be
// defined after their test methods
public function testUserLogin($user, $pass): void {
$this->assertNotEmpty($user);
}
// Another provider before test - WRONG ORDER
public function dataProviderEmailValidation(): array {
return [['test@example.com']];
}
/**
* @dataProvider dataProviderEmailValidation
*/
// ERROR: Data provider method "dataProviderEmailValidation" (line 24)
// appears before test method "testEmailValidation" (line 31). Providers
// should be defined after their test methods
public function testEmailValidation($email): void {
$this->assertNotEmpty($email);
}
}Note: All three rules:
@dataProvider annotations and PHP 8 #[DataProvider] attributesClassName::methodNameTest or TestCase, or extending TestCase)Full Changelog: 0.4.0...0.5.0
Updated to be compatible with PHPCS4. @AlexSkrypnyk
Full Changelog: 0.3.0...0.4.0
Allow installing in PHP 8.2 @AlexSkrypnyk
Simplify functional test assertions. @AlexSkrypnyk
Full Changelog: 0.1.0...0.2.0
Separated rules into LocalVariableSnakeCase and ParameterSnakeCase . @AlexSkrypnyk
LocalVariableSnakeCase and ParameterSnakeCase. @AlexSkrypnyk (#2)Full Changelog: ...0.1.0
Your coding agent can read these notes before it upgrades. Set up the MCP server →