PHPStan & Rector
Custom rules and extensions for static analysis and automated refactoring.
PHPStan
PHPStan level 6 configuration with Elegant Objects rules.
Configuration
Main configuration in tools/phpstan/phpstan.neon:
AssertTypeSpecifying Extension
PHPStan extension to support Webmozart Assert:
use Atournayre\PHPStan\Extension\AssertTypeSpecifyingExtension;
// Extension informs PHPStan about types after assertions
Assert::notNull(value: $user);
// PHPStan now knows $user is not null
Assert::isInstanceOf(value: $entity, class: User::class);
// PHPStan now knows $entity is User
Elegant Objects Rules
File tools/phpstan/elegant-object.neon with strict rules:
Private Constructors
// ✅ Correct
final class User
{
private function __construct(private readonly string $name) {}
public static function new(string $name): self
{
return new self(name: $name);
}
}
// ❌ PHPStan error: Constructor must be private
final class BadUser
{
public function __construct(string $name) {}
}
No Getters/Setters
// ✅ Correct
public function name(): string { return $this->name; }
// ❌ PHPStan error: Method name starts with get
public function getName(): string { return $this->name; }
Immutable Properties
// ✅ Correct
private readonly string $name;
// ❌ PHPStan error: Property is mutable
private string $name;
Final Classes
// ✅ Correct
final class User {}
abstract class BaseUser {}
// ❌ PHPStan error: Class must be final or abstract
class BadUser {}
Max 5 Public Methods
// ✅ Correct - 4 public methods
final class User
{
public function name(): string {}
public function email(): string {}
public function isActive(): bool {}
public function activate(): void {}
}
// ❌ PHPStan error: Too many public methods (6+)
Commands
# Full analysis
make phpstan
# or
php vendor/bin/phpstan analyse --configuration=tools/phpstan/phpstan.neon
# Update baseline
make phpstan-update-baseline
# With increased memory
php vendor/bin/phpstan analyse --memory-limit=4G
Rector
Automated refactoring with custom rules.
Configuration
Configuration in tools/rector.php:
use Rector\Config\RectorConfig;
use Atournayre\Rector\Sets;
return RectorConfig::configure()
->withPaths([__DIR__ . '/../src'])
->withSets([
Sets::VERSION_2_7_0,
Sets::VERSION_2_8_0,
Sets::VERSION_2_9_0,
]);
Rule: ReplaceTraitUseByAliasName
Replaces trait usage by their alias:
// Before
use Atournayre\Primitives\Traits\Collection as CollectionTrait;
// After automatic refactoring
use Atournayre\Primitives\Traits\Collection\CollectionTrait;
Commands
# Dry run (see changes without applying)
make rector
# or
php vendor/bin/rector process src --dry-run
# Apply changes
php vendor/bin/rector process src
CI/CD Integration
GitHub Actions
name: Static Analysis
on: [push, pull_request]
jobs:
phpstan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: php-actions/composer@v6
- name: PHPStan
run: make phpstan
rector:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: php-actions/composer@v6
- name: Rector (dry-run)
run: make rector
Development Workflow
Before Commit
# Check compliance
make qa # Runs PHPStan, Rector, PHP-CS-Fixer
# If PHPStan errors
make phpstan
# If refactoring needed
make rector # Dry run
php vendor/bin/rector process src # Apply
Benefits
PHPStan
- Type safety: Detect errors before runtime
- Elegant Objects: Respect design principles
- Documentation: Self-documented code through types
- Prevention: Bugs detected during development
Rector
- Automation: Automated massive refactoring
- Migration: Simplified version upgrades
- Consistency: Uniform code across codebase
- Productivity: Time saved on manual refactoring