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 example
<?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 example
<?php

declare(strict_types=1);

if ($isPaid === true) {
    echo 'Paid';
}

to this:

PHP example
<?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.php config.
  • Include src and tests paths.
  • 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 example
<?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.