# PHPStan Naming Convention Rules

Automated enforcement of framework naming conventions via PHPStan static analysis.

## Installed Rules

### 1. ControllerNamingRule

**Enforces:**
- ✅ View Controllers: `Show{Feature}` pattern
- ✅ Action Controllers: `{Verb}{Feature}` pattern
- ❌ NO "Controller" suffix
- ❌ Show* controllers must only have GET routes
- ❌ Show* controllers must return ViewResult

**Examples:**
```php
// ✅ CORRECT
class ShowContact { }        // View Controller
class SubmitContact { }      // Action Controller
class CreateUser { }         // Action Controller

// ❌ WRONG
class ContactController { }  // Has "Controller" suffix
class ShowContact {
    #[Route('/contact', POST)]  // Show* with POST route
    public function submit() { }
}
```

**Error Identifiers:**
- `naming.controller.noSuffix`
- `naming.controller.showPattern`
- `naming.controller.pattern`

---

### 2. ExceptionNamingRule

**Enforces:**
- ✅ Domain Exceptions: NO "Exception" suffix
- ✅ Technical Exceptions: WITH "Exception" suffix
- ✅ Hybrid approach based on namespace

**Examples:**
```php
// ✅ CORRECT - Domain Exceptions
namespace App\Domain\User\Exceptions;
class UserNotFound extends FrameworkException { }
class PaymentFailed extends FrameworkException { }

// ✅ CORRECT - Technical Exceptions
namespace App\Framework\Database\Exception;
class DatabaseException extends FrameworkException { }
class ValidationException extends FrameworkException { }

// ❌ WRONG
namespace App\Domain\User\Exceptions;
class UserNotFoundException { }  // Domain exception with suffix

namespace App\Framework\Database;
class DatabaseError { }  // Technical exception without suffix
```

**Error Identifiers:**
- `naming.exception.technicalSuffix`
- `naming.exception.domainNoSuffix`

---

### 3. CommandNamingRule

**Enforces:**
- ✅ CQRS Commands: NO "Command" suffix
- ✅ Console Commands: WITH "Command" suffix
- ✅ CQRS Commands follow Verb-Noun pattern

**Examples:**
```php
// ✅ CORRECT - CQRS Commands
namespace App\Application\Contact;
class SendEmail { }
class CreateUser { }
class StoreContact { }

// ✅ CORRECT - Console Commands
namespace App\Framework\Console\Commands;
class HealthCheckCommand implements ConsoleCommand { }
class MakeMigrationCommand implements ConsoleCommand { }

// ❌ WRONG
namespace App\Application\Contact;
class SendEmailCommand { }  // CQRS with suffix

namespace App\Framework\Console\Commands;
class HealthCheck { }  // Console without suffix
```

**Error Identifiers:**
- `naming.command.consoleSuffix`
- `naming.command.cqrsNoSuffix`
- `naming.command.verbNounPattern`

---

### 4. ValueObjectNamingRule

**Enforces:**
- ✅ NO "VO" suffix
- ✅ NO "ValueObject" suffix
- ✅ Descriptive names

**Examples:**
```php
// ✅ CORRECT
final readonly class Email { }
final readonly class Price { }
final readonly class Duration { }

// ❌ WRONG
final readonly class EmailVO { }
final readonly class PriceValueObject { }
```

**Error Identifiers:**
- `naming.valueObject.noVoSuffix`
- `naming.valueObject.noValueObjectSuffix`

---

### 5. InterfaceNamingRule

**Enforces:**
- ✅ NO "Interface" suffix
- ✅ Descriptive, behavior-focused names

**Examples:**
```php
// ✅ CORRECT
interface Cache { }
interface Logger { }
interface Repository { }
interface EventDispatcher { }

// ❌ WRONG
interface CacheInterface { }
interface LoggerInterface { }
interface RepositoryInterface { }
```

**Error Identifiers:**
- `naming.interface.noSuffix`

---

## Running PHPStan

### Full Analysis
```bash
make phpstan
# or
./vendor/bin/phpstan analyse
```

### Specific File
```bash
./vendor/bin/phpstan analyse src/Application/Contact/ShowContact.php
```

### Generate Baseline (for existing violations)
```bash
./vendor/bin/phpstan analyse --generate-baseline
```

## Ignoring Specific Violations

### Per-file ignore in phpstan.neon
```neon
parameters:
    ignoreErrors:
        -
            message: '#Controller ".*" should not have "Controller" suffix#'
            path: src/Application/Legacy/*
```

### Inline ignore
```php
/** @phpstan-ignore-next-line naming.controller.noSuffix */
class LegacyUserController { }
```

## Configuration

Rules are registered in `phpstan-rules.neon`:

```neon
rules:
    - App\Framework\Quality\PHPStan\Rules\Naming\ControllerNamingRule
    - App\Framework\Quality\PHPStan\Rules\Naming\ExceptionNamingRule
    - App\Framework\Quality\PHPStan\Rules\Naming\CommandNamingRule
    - App\Framework\Quality\PHPStan\Rules\Naming\ValueObjectNamingRule
    - App\Framework\Quality\PHPStan\Rules\Naming\InterfaceNamingRule
```

## Gradual Migration

**Approach 1: Baseline**
1. Run `./vendor/bin/phpstan analyse --generate-baseline`
2. Fix violations incrementally
3. Remove from baseline as you fix

**Approach 2: Exclude Paths**
```neon
parameters:
    excludePaths:
        - src/Application/Legacy/*
```

**Approach 3: Per-Rule Ignores**
```neon
parameters:
    ignoreErrors:
        - '#naming.controller.noSuffix#'  # Temporarily ignore
```

## Benefits

✅ **Automated Enforcement** - Naming conventions checked on every analysis
✅ **Clear Error Messages** - Actionable feedback with suggestions
✅ **IDE Integration** - Real-time feedback in PhpStorm/VSCode
✅ **CI/CD Integration** - Fail builds on violations
✅ **Gradual Migration** - Baseline support for existing code

## Related Documentation

- [Naming Conventions](../../../../../docs/claude/naming-conventions.md) - Full naming guidelines
- [PHPStan Documentation](https://phpstan.org/user-guide/getting-started) - PHPStan basics
- [Custom Rules](https://phpstan.org/developing-extensions/rules) - Creating custom rules
