rector/phpstan-rules
PHPStan rules for Rector projects that catch common mistakes in Rector rules and configuration, helping you keep automated refactorings safer and more consistent. Use these ready-made checks to validate custom rules and configs before they rewrite your codebase. Ideal for teams maintaining custom Re...
This is a narrow, opinionated package: it ships PHPStan rules that encode Rector's own design conventions and its static-reflection discipline. It is not a general-purpose rule pack for application code — it exists so that Rector core and third-party Rector extension packages stay consistent.
Minimal setup:
composer require --dev rector/phpstan-rules
If phpstan/extension-installer is allowed in your project, the rules register automatically. Otherwise include the config manually:
# phpstan.neon
includes:
- vendor/rector/phpstan-rules/config/config.neon
Where to look first:
vendor/rector/phpstan-rules/src/Rules/ — every rule is a small, readable class; this is the real documentation.vendor/rector/phpstan-rules/config/ — the registration file shows the exact service IDs to reference or exclude.phpstan.neon in the rectorphp/rector repository — canonical real-world usage.First use case: you maintain a Rector rule package (or a project with custom AbstractRector subclasses). Point PHPStan at that directory and let the package enforce two things:
new \ReflectionClass(...), \ReflectionMethod, etc. Rector analyses source that may not be autoloadable, so reflection must go through PHPStan's ReflectionProvider / Scope.getNodeTypes(), returns Node|null from refactor(), is stateless/final, and doesn't reach for Rector config or getName()-style shortcuts.A baseline-level phpstan.neon for a rule package:
includes:
- vendor/rector/phpstan-rules/config/config.neon
- vendor/phpstan/phpstan-strict-rules/rules.neon
parameters:
level: 8
paths:
- src
- rules
Run vendor/bin/phpstan analyse and treat the first output as an inventory of legacy patterns, not as errors to silence one by one.
Scope rules by path, not by disabling them. The reflection rules belong on src/Rules/** (where Rector code lives); the design rules belong on the same tree. If you have non-Rector code in the same PHPStan run, use per-path includes in separate config files and analyse each in its own PHPStan invocation rather than excluding rule classes globally.
Layer configs per package stage. A common layout:
phpstan.neon — base, strict rules, levelphpstan-rector.neon — includes rector/phpstan-rules, paths limited to rule directoriesphpstan-baseline.neon — only for pre-existing debt, regenerated via --generate-baselineWire it into CI as a separate job. Because these rules are structural, they fail fast and cheaply; running them alongside (not inside) your unit-test matrix keeps signal clear:
- run: composer install --no-interaction
- run: vendor/bin/phpstan analyse -c phpstan-rector.neon --no-progress --error-format=github
Model your own rules on the shipped ones. When you need a house rule ("every rule must be final", "no ContainerInterface in a rule"), open the closest rule in src/Rules/, copy its shape (implements Rule, getRuleDefinition(), processNode(Node, Scope)), and register it in your own services block. The package doubles as a worked example of the Rule API.
Migrate reflection incrementally. Replace new ReflectionClass($fqcn) with $scope->getClassReflection($fqcn) or the injected ReflectionProvider one call site at a time, running PHPStan after each; the rule messages name the offending line, so the loop is tight. Keep reflectionProvider->hasClass() guards near the call site rather than centralising them.
Test custom rules with PHPStan's own harness. Use PHPStan\Testing\RuleTestCase and construct the container (or StaticReflectionProvider) explicitly — the package's reflection rules assume a container is present, and your tests must mirror that.
AbstractRector, RectorConfig, the Rule contract). Applied to a Laravel or Symfony app, you will get false positives. Scope paths aggressively or don't install it there at all.rector/rector pulls this in as a dev dependency; you may already have it under vendor/. Check before adding a duplicate requirement — and expect the bundled version to move with Rector majors.ReflectionProvider, ClassReflection, ExtendedMethodReflection) has shifted signatures across PHPStan majors. Pin phpstan/phpstan and this package together, and re-run after any PHPStan bump before trusting a green build.ReflectionClass works on test fixtures; ReflectionProvider needs a configured container and autoload map. Fixtures that pass under native reflection can silently resolve to null class reflections in tests — assert on hasClass() before getClass().getNodeTypes()" will stay missing. Fix or exclude the path.phpstan-strict-rules or phpstan-deprecation-rules; duplicate service IDs produce confusing "service already registered" errors. Let extension-installer handle registration when possible.-v --debug, then check whether the rule is path-scoped correctly. Excluding a rule by class name is a last resort — prefer narrowing paths.Rule implementations that follow the shipped conventions; the package gives you the pattern vocabulary (getRuleDefinition() with a code sample, getNodeTypes(), stateless refactor()), not a plugin surface.dev-main, and revisit on each Rector upgrade.How can I help you explore Laravel packages today?