code quality and tooling
Rector
Rector is an automated refactoring tool for PHP. It parses PHP code, applies configured transformation rules, and rewrites the code. It is commonly used for PHP version upgrades, framework migrations, adding type declarations, removing dead code, and applying code-quality refactors.
Rector is more powerful than a formatter. A formatter changes layout. Rector can change code structure. That means every Rector diff needs careful review.
Running Rector
In a Composer project, Rector is usually run from vendor/bin.
vendor/bin/rector process --dry-run
Dry-run mode shows what Rector would change without writing to files. Use it first.
To apply changes:
vendor/bin/rector process
After applying changes, run the test suite, static analysis, and formatter.
Configuration
Rector uses a rector.php config file.
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->withPreparedSets(
codeQuality: true,
deadCode: true,
typeDeclarations: true,
);
This config tells Rector which paths to process and which prepared rule sets to use.
What Rector can change
Rector can perform changes such as:
- replacing old syntax with newer syntax
- adding type declarations
- removing unused private methods
- simplifying conditions
- converting some annotations to attributes
- updating framework-specific patterns
- preparing code for a newer PHP version
These changes can be useful, but they are not automatically risk-free.
Review one kind of change at a time
Rector can create large diffs. Large mixed diffs are hard to review.
Prefer small runs:
- one PHP version step at a time
- one prepared set at a time
- one directory at a time
- one custom rule at a time
This keeps the review focused and makes it easier to spot unsafe changes.
Example change
Rector might simplify code like this:
<?php
declare(strict_types=1);
if ($isPaid === true) {
echo 'Paid';
}
to this:
<?php
declare(strict_types=1);
if ($isPaid) {
echo 'Paid';
}
That is probably safe when $isPaid is known to be a boolean. If the value can be something else, the review needs more care.
Run a formatter after Rector
Rector works on PHP syntax trees. Its output may not match the project's preferred formatting perfectly. Run the project formatter after Rector.
vendor/bin/rector process
vendor/bin/php-cs-fixer fix
or:
vendor/bin/rector process
vendor/bin/phpcbf
Use whichever formatter the project already uses.
Verification matters
After Rector changes code, run the checks that prove behaviour still holds:
- syntax linting if available
- formatter
- static analysis
- unit tests
- integration tests for affected areas
- manual checks for risky application paths
Do not merge a Rector change just because the tool produced a diff.
When to skip rules or paths
Some directories should not be processed:
- generated code
- vendor code
- cache directories
- migrations that must remain historically accurate
- legacy areas where a rule creates risky changes
Rector config can skip paths or rules. Use skips deliberately and explain them when they protect important behaviour.
What to remember
Rector automates refactoring and upgrades. It is powerful, so use dry-run first, keep rule sets narrow, review the diff, run a formatter afterward, and verify with tests and static analysis.
Before moving on, make sure you can explain why Rector is riskier than a pure formatter and why dry-run should be the first command.
Practice
Task: Configure A Rector Dry Run
Write a safe first Rector setup for a project that wants to analyse src and tests.
Requirements
- Show a minimal
rector.phpconfig. - Include
srcandtestspaths. - Enable code quality and type declaration prepared sets.
- Give the command to preview changes without writing files.
- Give the command to apply changes.
- List two checks to run after applying Rector.
Show solution
rector.php
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
])
->withPreparedSets(
codeQuality: true,
typeDeclarations: true,
);
Preview changes without writing files:
vendor/bin/rector process --dry-run
Apply changes:
vendor/bin/rector process
After applying Rector, run the formatter and the test suite. Static analysis is also a strong follow-up check.