
September 08, 2026
10 min read
By Kokil Thapa | Last reviewed: September 2026
Legacy PHP still runs millions of production apps, and manual upgrades are slow, risky, and expensive. PHP Rector for automated refactoring applies codemods across your codebase so you can move from PHP 7.x patterns to PHP 8.5 syntax without rewriting every file by hand. On real client projects I have paired Rector with ongoing application maintenance, Composer updates, and staged deploys. This guide covers install, rule selection, dry runs, Laravel-specific sets, and CI wiring you can copy today.
What Is PHP Rector for Automated Refactoring?
Rector is an open-source PHP tool that parses your code with nikic/PHP-Parser, walks an abstract syntax tree, and applies transformation rules. Each rule targets one pattern: replace array() with short arrays, add return types, or rename deprecated Laravel facades.
Think of it as a compiler pass you control. You choose sets like LevelSetList::UP_TO_PHP_85 or LaravelLevelSetList::UP_TO_LARAVEL_120. Rector runs file by file and prints a unified diff. Nothing changes until you pass --dry-run off.
Rector complements static analysis. PHPStan at level 9 finds type errors Rector cannot fix alone. Laravel Pint or PHP-CS-Fixer handles formatting after Rector restructures code. Together they form a modernisation stack I use before touching PHP 8.4 and 8.5 features.
How Do You Install PHP Rector for Automated Refactoring?
Install Rector as a dev dependency with Composer 2.10. Your project needs PHP 8.2 or higher to run the Rector binary itself, even if you are upgrading older syntax inside files.
Composer install
composer require rector/rector --dev
vendor/bin/rector --version Create rector.php at the project root. This config file is the control centre for every automated refactoring run.
Minimal rector.php for PHP 8.5
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\Set\ValueObject\LevelSetList;
use Rector\Set\ValueObject\SetList;
return RectorConfig::configure()
->paths([
__DIR__ . '/app',
__DIR__ . '/src',
__DIR__ . '/routes',
])
->skip([
__DIR__ . '/vendor',
__DIR__ . '/storage',
__DIR__ . '/bootstrap/cache',
])
->sets([
SetList::CODE_QUALITY,
LevelSetList::UP_TO_PHP_85,
]); Run a dry run to see proposed changes without writing files:
vendor/bin/rector process --dry-run When the diff looks correct, apply it:
vendor/bin/rector process On a legal-tech portal I maintain, we ran Rector on a /app tree of roughly 400 files before a PHP 8.3 jump. The dry run surfaced 1,200 line changes. We split that into three PRs by directory to keep reviews manageable.
Which PHP Rector Rules Should You Use for Automated Refactoring?
Rule selection is where most teams stumble. Enabling every set at once produces giant diffs that are hard to review. Start narrow, expand in phases.
Core set categories
- Level sets —
UP_TO_PHP_83,UP_TO_PHP_84,UP_TO_PHP_85bring syntax forward step by step. - Code quality — removes dead code, simplifies conditions, converts to null-safe operators.
- Type declaration — adds param and return types where Rector can infer them safely.
- Framework sets — Laravel, Symfony, PHPUnit, and Doctrine each ship dedicated rules.
For Laravel 12 apps targeting Laravel 13, add the Laravel extension:
composer require rector/rector-laravel --dev use RectorLaravel\Set\LaravelLevelSetList;
use RectorLaravel\Set\LaravelSetList;
return RectorConfig::configure()
->sets([
LaravelLevelSetList::UP_TO_LARAVEL_120,
LaravelSetList::LARAVEL_CODE_QUALITY,
]); Official rule documentation lives at getrector.com/documentation. Browse sets before you enable them blindly.
| Rule set | Best for | Risk level | Run order |
|---|---|---|---|
LevelSetList::UP_TO_PHP_85 | PHP version bumps | Medium | 1 |
SetList::CODE_QUALITY | Readability wins | Low | 2 |
SetList::TYPE_DECLARATION | Stricter typing | Medium–High | 3 |
LaravelLevelSetList::UP_TO_LARAVEL_120 | Laravel 11→12 | Medium | After PHP sets |
| Custom rules | Project-specific patterns | Varies | Last |
Custom rules for project-specific patterns
When a codebase repeats the same anti-pattern, write a custom Rector rule. Example: replace a deprecated helper with a namespaced utility across 200 call sites. The custom rule guide walks through node visitors and test fixtures.
Pair type-declaration sets with readonly classes and enum adoption only after PHPStan passes on the current baseline. Rector adds types; PHPStan proves they hold.
How Do You Run PHP Rector for Automated Refactoring in CI?
CI integration prevents drift. Two common patterns exist: gate on dry-run failure, or auto-commit on a dedicated branch. I prefer the gate for production repos.
GitLab CI example
rector:
stage: test
image: php:8.5-cli
script:
- composer install --no-interaction --prefer-dist
- vendor/bin/rector process --dry-run --no-progress-bar
only:
- merge_requests
- main If Rector would change any file, the job exits with a non-zero code. Developers run Rector locally before push. This mirrors how AI code review in CI catches style issues early.
Pre-commit hook (optional)
#!/bin/sh
vendor/bin/rector process --dry-run --no-progress-bar
if [ $? -ne 0 ]; then
echo "Rector changes pending. Run: vendor/bin/rector process"
exit 1
fi For teams using custom Laravel applications, add Rector after composer install in your Deployer 7 pipeline on a staging host first. Never run a blind Rector pass on production.
- Commit current working state and tag the release.
- Run Rector dry-run on a feature branch.
- Apply changes and run PHPUnit or Pest.
- Run PHPStan at your agreed level.
- Deploy to staging; smoke-test critical flows.
- Merge and deploy production with PHP-FPM reload.
On sister sites sharing a GitLab CI pipeline, we cache vendor/ between jobs. Rector runs in under two minutes on typical Laravel apps when cache hits. See Ubuntu server setup for PHP apps for PHP 8.5 FPM pool tuning after deploy.
When Should PHP Rector Replace Manual Refactoring?
Rector wins on repetitive, syntactic changes across many files. Manual refactoring wins when business logic, domain rules, or architectural boundaries shift. Know which camp your task falls into.
Use PHP Rector for automated refactoring when you need to:
- Upgrade PHP from 8.2 to 8.5 before Laravel 13 requires PHP 8.3+.
- Remove deprecated dynamic properties and null-passed-to-string patterns.
- Adopt constructor property promotion across DTOs and value objects.
- Apply Laravel framework renames after a minor version bump.
- Normalize legacy WordPress plugin code before a PHP version change on shared hosting.
Stick to manual refactors when:
- You redesign service boundaries or extract bounded contexts.
- Database schema and Eloquent relationships change together.
- Payment gateway callbacks need idempotency rewrites, not syntax swaps.
- Legacy CodeIgniter apps need routing restructure, not just typed params.
For a platform like Nepal Gift Card, Rector handled PHP syntax while we manually rewrote order-state logic. For Court Marriage In Nepal, automated passes cleaned deprecated helpers without touching form validation rules tied to Nepali document workflows.
After Rector runs, validate JSON API responses with a JSON formatter during manual QA. Syntax upgrades can expose serialisation edge cases covered in PHP JSON handling for large payloads.
Common gotchas
Rector cannot infer intent when two refactor paths exist. Nullable return types on legacy methods sometimes need human review. Dynamic $this->$property access may skip rules silently.
Always exclude generated files, cached views, and migration stubs you plan to squash. Commit a rector-baseline.php or use ->skip() for directories under active rewrite.
After upgrading, tune OPcache for production and verify Composer autoloader settings. New class layouts from Rector can shift autoload performance slightly.
WordPress and WooCommerce 11.1 projects benefit from scoped Rector runs on custom plugins only. Never point Rector at wp-core or vendor plugin trees. Our WordPress development practice treats Rector as a plugin-modernisation step, not a core replacement tool.
Budget time realistically. A 50k-line Laravel app might need 3–5 phased PRs over two sprints. That beats a six-month manual rewrite costing Rs 400,000–800,000 (~USD 3,000–6,000) in contractor time for Nepal-based teams. Larger rewrites belong in a planned website migration with explicit test coverage goals via testing and optimisation services.
PHP 8.5 introduces continued deprecations documented on php.net releases. Rector level sets track many of these, but read the official migration notes for gaps Rector has not codified yet.
Key Takeaways
- Install Rector with Composer 2.10 and configure
rector.phppaths, skips, and phased rule sets before touching code. - Always run
--dry-runfirst; split large diffs into reviewable PRs by directory or rule set. - Combine Rector with PHPStan and your test suite—automated refactoring changes syntax, not business guarantees.
- Add a CI dry-run gate so uncommitted Rector output cannot merge to main.
- Use Rector for syntax and framework upgrades; reserve manual refactors for domain logic and architecture.
- Exclude vendor, storage, and legacy modules via
->skip()until you are ready to modernise them.
People Also Ask
Does Rector work with Laravel 12 and Laravel 13?
Yes. Install rector/rector-laravel and enable LaravelLevelSetList sets matching your target version. Run PHP level sets first, then Laravel-specific rules. Laravel 13 requires PHP 8.3+, so align both upgrades in one branch or sequential PRs.
Can Rector break my production app?
Rector only changes what you apply after dry-run review. Risk comes from skipping tests or enabling aggressive type-declaration sets on untyped legacy code. Staging deploys and PHPUnit coverage catch most regressions before production.
Is Rector better than PHPStan or Psalm?
They solve different problems. Rector rewrites code. PHPStan and Psalm analyse it without changing files. Use all three: Rector modernises syntax, static analysis validates correctness, and Pint formats the result.
How long does a typical PHP upgrade with Rector take?
A well-tested Laravel app with 500–2,000 PHP files often needs one to three days of Rector runs plus a week of review and QA. Timeline grows with missing tests, custom framework wrappers, and excluded legacy directories.
Ship Modern PHP Without the Rewrite Tax
PHP Rector for automated refactoring turns painful version upgrades into repeatable, reviewable steps. Start with a dry run, phase your rule sets, wire CI, and keep PHPStan in the loop. That is the workflow I use on production Laravel apps before every PHP minor bump.
Need help modernising a legacy codebase or planning a Laravel 13 upgrade path? Contact us for a scoped audit, or browse the portfolio for examples of maintained PHP applications. For related reading, see PHP type coercion gotchas and about the author.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

