simtel / phpstan-rules
Extended rules for phpstan
Requires
- php: >8.3
- phpstan/phpdoc-parser: ^2.2
- phpstan/phpstan: ^2.0
Requires (Dev)
- phpunit/phpunit: ^12.0
- symplify/easy-coding-standard: ^12.5
This package is auto-updated.
Last update: 2025-09-02 09:13:28 UTC
README
A collection of custom PHPStan rules that enforce coding standards and improve code quality in PHP projects. This extension provides additional static analysis rules focused on naming conventions, annotations, and PHPDoc consistency.
🎯 Features
This package includes three powerful rules that help maintain high code quality:
1. Command-Handler Relationship Rule
Rule: CommandClassShouldBeHelpCommandHandlerClass
Enforces that classes ending with "Command" must have a @see
PHPDoc tag pointing to their corresponding CommandHandler class.
Example:
/** * @see CreateUserCommandHandler */ class CreateUserCommand { // Command implementation }
Exception: Classes with an __invoke
method are exempt from this rule.
2. Event Listener Attribute Rule
Rule: EventListenerClassShouldBeIncludeAsListenerAttribute
Ensures that classes ending with "EventListener" are properly annotated with the #[AsEventListener]
attribute.
Example:
use Symfony\Component\EventDispatcher\Attribute\AsEventListener; #[AsEventListener] class UserRegisteredEventListener { // Event listener implementation }
3. Redundant PHPDoc Return Type Rule
Rule: NotShouldPhpdocReturnIfExistTypeHint
Prevents redundant or conflicting @return
PHPDoc annotations when native return type hints are already declared.
Good:
public function getUser(): User { return $this->user; }
Bad:
/** * @return User */ public function getUser(): User // Redundant @return annotation { return $this->user; }
📦 Installation
System Requirements
- PHP: >8.3
- PHPStan: ^2.0
- PHPStan PHPDoc Parser: ^2.2
Install via Composer
Install the package as a development dependency:
composer require --dev simtel/phpstan-rules
⚙️ Configuration
Option 1: Include All Rules (Recommended)
Add the bundled configuration to your phpstan.neon
or phpstan.dist.neon
:
includes: - vendor/simtel/phpstan-rules/rules.neon
Option 2: Manual Rule Registration
For granular control, register specific rules:
parameters: rules: - Simtel\PHPStanRules\Rule\CommandClassShouldBeHelpCommandHandlerClass - Simtel\PHPStanRules\Rule\EventListenerClassShouldBeIncludeAsListenerAttribute - Simtel\PHPStanRules\Rule\NotShouldPhpdocReturnIfExistTypeHint
Complete Configuration Example
includes: - vendor/simtel/phpstan-rules/rules.neon parameters: level: 8 paths: - src/ excludePaths: - tests/ ignoreErrors: # Exclude specific patterns if needed - '#Command class should be include phpDoc with @see attribute#' path: src/Deprecated/
🔧 Development
Setting Up Development Environment
-
Clone the repository:
git clone <repository-url> phpstan-rules cd phpstan-rules
-
Install dependencies:
composer install
Development Commands
Running Tests
# Run all tests vendor/bin/phpunit # Run specific test class vendor/bin/phpunit tests/Rules/CommandClassShouldBeHelpCommandHandlerClassTest.php
Code Style
# Check coding standards vendor/bin/ecs check # Fix coding standards automatically vendor/bin/ecs fix
Static Analysis
# Run PHPStan on the project itself
vendor/bin/phpstan analyse
Project Structure
.
├── src/Rule/ # Rule implementations
│ ├── CommandClassShouldBeHelpCommandHandlerClass.php
│ ├── EventListenerClassShouldBeIncludeAsListenerAttribute.php
│ └── NotShouldPhpdocReturnIfExistTypeHint.php
├── tests/
│ ├── Fixture/ # Test code samples
│ │ ├── EventListener/
│ │ └── Return/
│ ├── Rules/ # Unit tests for rules
│ └── data/ # Additional test data
├── composer.json # Package configuration
├── ecs.php # ECS configuration
└── README.md # This file
Creating New Rules
-
Implement the Rule Interface:
<?php namespace Simtel\PHPStanRules\Rule; use PHPStan\Rules\Rule; /** * @implements Rule<NodeType> */ final class YourNewRule implements Rule { public function getNodeType(): string { return NodeType::class; } public function processNode(Node $node, Scope $scope): array { // Rule implementation } }
-
Create Test Cases:
<?php namespace Simtel\PHPStanRules\Tests\Rules; use PHPStan\Rules\Rule; use PHPStan\Testing\RuleTestCase; class YourNewRuleTest extends RuleTestCase { protected function getRule(): Rule { return new YourNewRule(); } public function testValidCase(): void { $this->analyse([__DIR__ . '/../Fixture/Valid.php'], []); } }
-
Add Fixture Files: Create test PHP files in
tests/Fixture/
to validate rule behavior.
Testing Strategy
The project uses PHPUnit with PHPStan's RuleTestCase
base class:
- Fixture Files: Real PHP code examples in
tests/Fixture/
- Data Files: Additional test scenarios in
tests/data/
- Unit Tests: Each rule has corresponding test class in
tests/Rules/
Contributing Guidelines
- Code Standards: Follow PSR-4 autoloading and use ECS for code style
- Testing: All rules must have comprehensive tests with both positive and negative cases
- Documentation: Update README.md and add inline documentation for new rules
- Performance: Ensure rules execute efficiently within PHPStan's analysis pipeline
🐛 Troubleshooting
Common Issues
"Rule Not Found" Errors
Cause: Incorrect namespace or class name in phpstan.neon
.
Solution: Verify the fully qualified class names and check for typos.
Rules Not Being Applied
Cause: Configuration file not included or rules not registered.
Solution: Ensure vendor/simtel/phpstan-rules/rules.neon
is included in your PHPStan configuration.
Performance Issues
Cause: Rules may be analyzing too many files or performing expensive operations.
Solution: Use excludePaths
to limit analysis scope or optimize rule logic.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
📞 Support
If you encounter any issues or have questions, please open an issue on GitHub.