Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 47 additions & 33 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Project Overview

This is a PHP_CodeSniffer (PHPCS) standard package (`drevops/phpcs-standard`) that enforces custom coding conventions, specifically focused on snake_case naming for local variables and parameters.
This is a PHP_CodeSniffer (PHPCS) standard package (`drevops/phpcs-standard`) that enforces custom coding conventions, specifically focused on configurable naming (snakeCase or camelCase) for local variables and parameters.

**Key Goals:**
- Enforce snake_case for local variables and function/method parameters
- Exclude class properties from snake_case enforcement (properties follow different conventions)
- Enforce consistent naming conventions for local variables and function/method parameters
- Support configurable formats: snakeCase (default) or camelCase
- Exclude class properties from naming enforcement (properties follow different conventions)
- Preserve inherited parameter names from interfaces and parent classes
- Provide auto-fixing support via `phpcbf`
- Provide a standalone, reusable PHPCS standard for the DrevOps ecosystem
Expand Down Expand Up @@ -49,8 +50,8 @@ Coverage reports are generated in:
Run a single test file:
```bash
./vendor/bin/phpunit tests/Unit/AbstractVariableSnakeCaseSniffTest.php
./vendor/bin/phpunit tests/Unit/LocalVariableSnakeCaseSniffTest.php
./vendor/bin/phpunit tests/Unit/ParameterSnakeCaseSniffTest.php
./vendor/bin/phpunit tests/Unit/LocalVariableNamingSniffTest.php
./vendor/bin/phpunit tests/Unit/ParameterNamingSniffTest.php
```
Comment on lines 50 to 55
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Align documentation with configurable naming and updated test names

The overall description now reflects configurable naming (snakeCase or camelCase), but a few spots are still out of sync:

  • “Run a single test file” still references AbstractVariableSnakeCaseSniffTest.php; the base test file appears to have been renamed to AbstractVariableNamingSniffTest.php, so this example should be updated.
  • In the directory structure, LocalVariableNamingSniff.php and ParameterNamingSniff.php are described as enforcing snake_case only; elsewhere in the doc you describe them as configurable. Consider rewording those bullets to “enforces configurable naming (snakeCase or camelCase)” for consistency.
  • The unit test overview still calls out AbstractVariableSnakeCaseSniffTest.php and describes only snake_case-focused tests, while the new tests also cover camelCase and error-code selection/integration for both sniffs.
  • The “Code Style Conventions” section still says “Use snake_case for local variables and method parameters”. Given the standard is now configurable, you might want to clarify that snake_case is the default, but camelCase can be enforced by setting $format = 'camelCase' in the sniffs’ configuration.

Updating these references will keep the guidance consistent with the new behavior and reduce confusion for contributors and tooling that relies on this file.

Also applies to: 90-93, 96-99, 181-195, 292-293

🤖 Prompt for AI Agents
In CLAUDE.md around lines 50-55 (also apply changes at 90-93, 96-99, 181-195,
292-293): update references and wording to reflect that naming is configurable
(snake_case or camelCase) and that test filenames changed; replace occurrences
of AbstractVariableSnakeCaseSniffTest.php with
AbstractVariableNamingSniffTest.php in examples and the unit test overview,
change descriptions for LocalVariableNamingSniff.php and
ParameterNamingSniff.php from “enforces snake_case only” to “enforces
configurable naming (snake_case or camelCase)” or similar, adjust the unit test
overview text to mention tests cover both snake_case and camelCase and
error-code selection/integration, and modify the Code Style Conventions line to
state that snake_case is the default but camelCase can be enforced via $format =
'camelCase' in sniff configuration.


Run only unit tests or functional tests:
Expand Down Expand Up @@ -86,15 +87,15 @@ composer normalize --dry-run
### Directory Structure
- `src/DrevOps/` - Source code for the PHPCS standard
- `Sniffs/NamingConventions/`
- `AbstractSnakeCaseSniff.php` - Base class with shared functionality
- `LocalVariableSnakeCaseSniff.php` - Enforces snake_case for local variables
- `ParameterSnakeCaseSniff.php` - Enforces snake_case for parameters
- `AbstractVariableNamingSniff.php` - Base class with shared functionality
- `LocalVariableNamingSniff.php` - Enforces snake_case for local variables
- `ParameterNamingSniff.php` - Enforces snake_case for parameters
- `ruleset.xml` - DrevOps standard definition
- `tests/` - PHPUnit tests organized by type:
- `Unit/` - Unit tests for individual sniff methods (using reflection)
- `AbstractVariableSnakeCaseSniffTest.php` - Tests shared base class methods
- `LocalVariableSnakeCaseSniffTest.php` - Tests local variable sniff
- `ParameterSnakeCaseSniffTest.php` - Tests parameter sniff
- `LocalVariableNamingSniffTest.php` - Tests local variable sniff
- `ParameterNamingSniffTest.php` - Tests parameter sniff
- `UnitTestCase.php` - Base test class with helper methods
- `Functional/` - Integration tests that run actual phpcs commands
- `Fixtures/` - Test fixture files with intentional violations
Expand All @@ -105,15 +106,22 @@ composer normalize --dry-run

The standard uses an **abstract base class pattern** with two concrete implementations:

#### AbstractSnakeCaseSniff
#### AbstractVariableNamingSniff

Base class (src/DrevOps/Sniffs/NamingConventions/AbstractSnakeCaseSniff.php) containing shared functionality:
Base class (src/DrevOps/Sniffs/NamingConventions/AbstractVariableNamingSniff.php) containing shared functionality:

**Public property:**
- `$format` - Configurable naming convention ('snakeCase' or 'camelCase', default: 'snakeCase')

**Core methods:**
- `register()` - Registers T_VARIABLE token for processing
- `isReserved()` - Identifies PHP reserved variables ($this, $_GET, etc.)
- `isSnakeCase()` - Validates snake_case format using regex
- `toSnakeCase()` - Converts camelCase to snake_case for suggestions
- `isCamelCase()` - Validates camelCase format using regex
- `isValidFormat()` - Validates variable name against configured format
- `toSnakeCase()` - Converts variable name to snake_case
- `toCamelCase()` - Converts variable name to camelCase
- `toFormat()` - Converts variable name to the configured format

**Helper methods:**
- `getParameterNames()` - Extracts parameter names from function signature
Expand All @@ -124,29 +132,33 @@ Base class (src/DrevOps/Sniffs/NamingConventions/AbstractSnakeCaseSniff.php) con
- `isProperty()` - Distinguishes class properties from local variables
- `isInheritedParameter()` - Detects parameters from interfaces/parent classes

#### LocalVariableSnakeCaseSniff
#### LocalVariableNamingSniff

Enforces snake_case for **local variables** inside functions/methods.
Enforces configurable naming convention for **local variables** inside functions/methods.

**What gets checked:**
- ✅ Local variables inside function/method bodies
- ❌ Function/method parameters (handled by ParameterSnakeCase)
- ❌ Function/method parameters (handled by ParameterNaming)
- ❌ Class properties (not enforced)
- ❌ Reserved PHP variables ($this, superglobals, etc.)

**Error code:** `DrevOps.NamingConventions.LocalVariableSnakeCase.NotSnakeCase`
**Error codes:**
- `DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase` (when format='snakeCase')
- `DrevOps.NamingConventions.LocalVariableNaming.NotCamelCase` (when format='camelCase')

#### ParameterSnakeCaseSniff
#### ParameterNamingSniff

Enforces snake_case for **function/method parameters**.
Enforces configurable naming convention for **function/method parameters**.

**What gets checked:**
- ✅ Function and method parameters (in signature only)
- ❌ Local variables (handled by LocalVariableSnakeCase)
- ❌ Local variables (handled by LocalVariableNaming)
- ❌ Parameters inherited from interfaces/parent classes/abstract methods
- ❌ Promoted constructor properties

**Error code:** `DrevOps.NamingConventions.ParameterSnakeCase.NotSnakeCase`
**Error codes:**
- `DrevOps.NamingConventions.ParameterNaming.NotSnakeCase` (when format='snakeCase')
- `DrevOps.NamingConventions.ParameterNaming.NotCamelCase` (when format='camelCase')

### PHPCS Standard Registration

Expand All @@ -169,16 +181,16 @@ Tests are organized by class hierarchy:
**AbstractVariableSnakeCaseSniffTest.php**
- Tests all shared base class methods using reflection
- Tests: `isSnakeCase()`, `toSnakeCase()`, `isReserved()`, `register()`, `getParameterNames()`, `isProperty()`, `isPromotedProperty()`, `isInheritedParameter()`
- Each test uses concrete sniff instances (LocalVariableSnakeCaseSniff or ParameterSnakeCaseSniff) to access protected methods
- Each test uses concrete sniff instances (LocalVariableNamingSniff or ParameterNamingSniff) to access protected methods

**LocalVariableSnakeCaseSniffTest.php**
**LocalVariableNamingSniffTest.php**
- Tests sniff-specific logic: error code constant and `process()` method
- Configured to run only LocalVariableSnakeCase sniff in isolation
- Configured to run only LocalVariableNaming sniff in isolation
- Validates that local variables are checked and parameters are skipped

**ParameterSnakeCaseSniffTest.php**
**ParameterNamingSniffTest.php**
- Tests sniff-specific logic: error code constant, `register()`, and `process()` method
- Configured to run only ParameterSnakeCase sniff in isolation
- Configured to run only ParameterNaming sniff in isolation
- Validates that parameters are checked and local variables are skipped
- Includes tests for inherited parameter detection

Expand All @@ -190,15 +202,15 @@ Tests are organized by class hierarchy:

#### 2. Functional Tests

**LocalVariableSnakeCaseSniffFunctionalTest.php**
**LocalVariableNamingSniffFunctionalTest.php**
- Run actual `phpcs` commands as external processes
- Test complete PHPCS integration with JSON output parsing
- Verify LocalVariableSnakeCase sniff detection and error codes
- Verify LocalVariableNaming sniff detection and error codes

**ParameterSnakeCaseSniffFunctionalTest.php**
**ParameterNamingSniffFunctionalTest.php**
- Run actual `phpcs` commands as external processes
- Test complete PHPCS integration with JSON output parsing
- Verify ParameterSnakeCase sniff detection and error codes
- Verify ParameterNaming sniff detection and error codes

Tests include:
- Confirms violations are detected with correct error codes
Expand Down Expand Up @@ -239,7 +251,7 @@ When implementing or modifying sniffs:

1. Place sniff classes in `src/DrevOps/Sniffs/` following PHPCS naming conventions
- Format: `CategoryName/SniffNameSniff.php`
- Example: `NamingConventions/LocalVariableSnakeCaseSniff.php`
- Example: `NamingConventions/LocalVariableNamingSniff.php`
2. Consider using abstract base classes for shared functionality across related sniffs
3. Implement the `Sniff` interface from `PHP_CodeSniffer\Sniffs\Sniff`
4. Use `declare(strict_types=1);` at the top of all PHP files
Expand All @@ -254,8 +266,10 @@ When implementing or modifying sniffs:
10. Create fixture files in `tests/Fixtures/` with intentional violations
11. Follow error code naming: `StandardName.Category.SniffName.ErrorName`
- Examples:
- `DrevOps.NamingConventions.LocalVariableSnakeCase.NotSnakeCase`
- `DrevOps.NamingConventions.ParameterSnakeCase.NotSnakeCase`
- `DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase`
- `DrevOps.NamingConventions.LocalVariableNaming.NotCamelCase`
- `DrevOps.NamingConventions.ParameterNaming.NotSnakeCase`
- `DrevOps.NamingConventions.ParameterNaming.NotCamelCase`

## CI/CD

Expand Down
70 changes: 54 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

---
PHP_CodeSniffer standard enforcing:
- `snake_case` naming for local variables and function/method parameters
- Consistent naming conventions for local variables and function/method parameters (configurable: `snakeCase` or `camelCase`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Configurable naming docs are clear; fix markdown fenced-block spacing for lint

The new sections for LocalVariableNaming and ParameterNaming, including examples and error-code names, match the configurable snakeCase/camelCase behavior and look good. The only issue is the markdownlint MD031 warnings around fenced code blocks immediately following the “With …” lines; add a blank line before each ```php fence to satisfy the rule.

You can fix all four places like this:

-**With `snakeCase` (default):**
-```php
+**With `snakeCase` (default):**
+
+```php
@@
-**With `camelCase`:**
-```php
+**With `camelCase`:**
+
+```php
@@
-**With `snakeCase` (default):**
-```php
+**With `snakeCase` (default):**
+
+```php
@@
-**With `camelCase`:**
-```php
+**With `camelCase`:**
+
+```php

This should clear the MD031 “Fenced code blocks should be surrounded by blank lines” findings while keeping the rendered output the same.

Also applies to: 63-65, 73-91, 93-122, 130-155, 159-159

🤖 Prompt for AI Agents
In README.md around lines 22, 63-65, 73-91, 93-122, 130-155 and 159, the fenced
PHP code blocks that immediately follow the "With `snakeCase`/`camelCase`" lines
are not preceded by a blank line and trigger markdownlint MD031; insert a single
blank line between each "With ..." line and the following ```php fence so each
fenced code block is surrounded by blank lines, and apply this same change to
all the listed ranges to resolve the MD031 warnings while keeping content
unchanged.

- PHPUnit data provider naming conventions and organization

## Installation
Expand Down Expand Up @@ -60,8 +60,8 @@ Use individual sniffs:
```xml
<ruleset name="Custom Standards">
<!-- Naming Conventions -->
<rule ref="DrevOps.NamingConventions.LocalVariableSnakeCase"/>
<rule ref="DrevOps.NamingConventions.ParameterSnakeCase"/>
<rule ref="DrevOps.NamingConventions.LocalVariableNaming"/>
<rule ref="DrevOps.NamingConventions.ParameterNaming"/>

<!-- Testing Practices -->
<rule ref="DrevOps.TestingPractices.DataProviderPrefix"/>
Expand All @@ -70,55 +70,93 @@ Use individual sniffs:
</ruleset>
```

## `LocalVariableSnakeCase`
### Configure naming convention

Enforces `snake_case` for local variables inside functions/methods.
By default, both sniffs enforce `snakeCase`. Configure to use `camelCase`:

```xml
<ruleset name="Custom Standards">
<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>
</ruleset>
```

## `LocalVariableNaming`

Enforces consistent naming convention for local variables inside functions/methods.

**With `snakeCase` (default):**
```php
function processOrder() {
$order_id = 1; // ✓ Valid
$orderId = 1; // ✗ Error: VariableNotSnakeCase
$orderId = 1; // ✗ Error: NotSnakeCase
}
```

**With `camelCase`:**
```php
function processOrder() {
$orderId = 1; // ✓ Valid
$order_id = 1; // ✗ Error: NotCamelCase
}
```

Excludes:
- Function/method parameters (handled by `ParameterSnakeCase`)
- Function/method parameters (handled by `ParameterNaming`)
- Class properties (not enforced)
- Reserved variables (`$this`, `$_GET`, `$_POST`, etc.)

### Error code
### Error codes

`DrevOps.NamingConventions.LocalVariableSnakeCase.NotSnakeCase`
- `DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase` (when `format="snakeCase"`)
- `DrevOps.NamingConventions.LocalVariableNaming.NotCamelCase` (when `format="camelCase"`)

### Ignore

```php
// phpcs:ignore DrevOps.NamingConventions.LocalVariableSnakeCase.NotSnakeCase
// phpcs:ignore DrevOps.NamingConventions.LocalVariableNaming.NotSnakeCase
$myVariable = 'value';
```

## `ParameterSnakeCase`
## `ParameterNaming`

Enforces `snake_case` for function/method parameters.
Enforces consistent naming convention for function/method parameters.

**With `snakeCase` (default):**
```php
function processOrder($order_id, $user_data) { // ✓ Valid
function processOrder($orderId, $userData) { // ✗ Error: ParameterNotSnakeCase
function processOrder($orderId, $userData) { // ✗ Error: NotSnakeCase
```

**With `camelCase`:**
```php
function processOrder($orderId, $userData) { // ✓ Valid
function processOrder($order_id, $user_data) { // ✗ Error: NotCamelCase
```

Excludes:
- Parameters inherited from interfaces/parent classes
- Parameters in interface/abstract method declarations
- Class properties (including promoted constructor properties)

### Error code
### Error codes

`DrevOps.NamingConventions.ParameterSnakeCase.NotSnakeCase`
- `DrevOps.NamingConventions.ParameterNaming.NotSnakeCase` (when `format="snakeCase"`)
- `DrevOps.NamingConventions.ParameterNaming.NotCamelCase` (when `format="camelCase"`)

### Ignore

```php
// phpcs:ignore DrevOps.NamingConventions.ParameterSnakeCase.NotSnakeCase
// phpcs:ignore DrevOps.NamingConventions.ParameterNaming.NotSnakeCase
function process($legacyParam) {}
```

Expand Down
13 changes: 12 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
{
"name": "drevops/phpcs-standard",
"description": "DrevOps PHP_CodeSniffer rules: enforce snake_case for variables and parameters.",
"description": "DrevOps PHP_CodeSniffer rules: enforce consistent naming (snakeCase or camelCase) for variables and parameters, PHPUnit data provider conventions.",
"license": "GPL-2.0-or-later",
"type": "phpcodesniffer-standard",
"keywords": [
"phpcs",
"phpcodesniffer",
"standards",
"snakeCase",
"camelCase",
"naming-conventions",
"code-quality",
"phpunit",
"data-providers"
],
"authors": [
{
"name": "Alex Skrypnyk",
Expand Down
Loading