Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

PHP Rector for Automated Refactoring

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.

PHP Rector Automated Refactoring FlowLegacy PHP7.x / 8.0 codeRector ASTParse + rulesRule SetsPHP / LaravelModern PHP8.5 readyDry Run FirstReview diff, run tests, then applyPHPStanCatch type gapsPHPUnit / PestVerify behaviourNever skip dry-run on production branches
PHP Rector for automated refactoring parses legacy code, applies rule sets, and validates output before merge

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 setsUP_TO_PHP_83, UP_TO_PHP_84, UP_TO_PHP_85 bring 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 setBest forRisk levelRun order
LevelSetList::UP_TO_PHP_85PHP version bumpsMedium1
SetList::CODE_QUALITYReadability winsLow2
SetList::TYPE_DECLARATIONStricter typingMedium–High3
LaravelLevelSetList::UP_TO_LARAVEL_120Laravel 11→12MediumAfter PHP sets
Custom rulesProject-specific patternsVariesLast
Rector Rule Set PhasesPhase 1PHP level setsLow riskPhase 2Code qualityMedium riskPhase 3Type + LaravelHigher riskEach phase: dry-run, test suite, small PR, deploySkip vendor/Always exclude third-partyCustom skip pathsLegacy modules you deferPhased PHP Rector runs beat one massive automated refactor
Phased PHP Rector rule sets reduce review load and production risk during automated refactoring

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.

  1. Commit current working state and tag the release.
  2. Run Rector dry-run on a feature branch.
  3. Apply changes and run PHPUnit or Pest.
  4. Run PHPStan at your agreed level.
  5. Deploy to staging; smoke-test critical flows.
  6. 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.

CI Pipeline with PHP RectorGit PushComposerInstall depsRectorDry-run gatePHPStanStatic checkTestsFail = Rector diff not committedDeveloper runs rector process locallyStaging DeploySmoke tests passProductionPHP-FPM reloadGate merges until Rector output matches committed code
GitLab CI pipeline gating merges on PHP Rector dry-run output for safe automated refactoring

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.

Rector vs Manual RefactoringWhat changed?Syntax onlyTypes, deprecationsLogic changeDomain rulesUse RectorAutomated refactorManual PRDesign + testsPHP Rector for automated refactoring handles syntax; you handle architectureCombine both on large migrations
Decision tree: use PHP Rector for automated refactoring on syntax, manual work on domain logic

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.php paths, skips, and phased rule sets before touching code.
  • Always run --dry-run first; 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

PHP Rector is an open-source tool that parses PHP with nikic/PHP-Parser, walks an abstract syntax tree, and applies configurable transformation rules to modernise syntax without manual rewrites.

Install Rector as a dev dependency with Composer 2.10 using composer require rector/rector --dev. Verify with vendor/bin/rector --version, then create a rector.php config file at your project root defining paths, skip directories, and rule sets. Start with a dry run via vendor/bin/rector process --dry-run before applying changes with vendor/bin/rector process. On a legal-tech portal I maintain, we ran this workflow on roughly 400 files in /app before a PHP 8.3 jump.

Your project needs PHP 8.2 or higher to execute vendor/bin/rector, even when the files you are upgrading contain older PHP 7.x syntax patterns.

Rector is free and open source. Your real cost is developer time for dry runs, phased PR reviews, and QA—not licensing fees.

Start narrow and expand in phases. Run LevelSetList::UP_TO_PHP_85 first for version bumps, then SetList::CODE_QUALITY for readability, then SetList::TYPE_DECLARATION once PHPStan validates your baseline. For Laravel apps, add LaravelLevelSetList::UP_TO_LARAVEL_120 and LaravelSetList::LARAVEL_CODE_QUALITY after PHP-level sets via rector/rector-laravel. Enabling every set at once produces giant diffs that are painful to review. Browse official sets at getrector.com/documentation before enabling them blindly.

Always start with vendor/bin/rector process --dry-run. Rector scans configured paths, applies selected rules in memory, and prints a unified diff showing every proposed change. Nothing is written to disk until you run vendor/bin/rector process without the dry-run flag. Review the diff carefully, split large outputs into directory-scoped PRs, and only apply when the changes match your upgrade plan. This workflow is the core safety mechanism that makes automated refactoring reviewable before merge.

Yes. Install rector/rector-laravel as a dev dependency and enable LaravelLevelSetList sets matching your target framework version, such as UP_TO_LARAVEL_120 for Laravel 12 apps. Run PHP level sets first, then Laravel-specific rules in a separate phase. Laravel 13 requires PHP 8.3 or higher, so align both the PHP and framework upgrades in one branch or sequential PRs. Pair Rector output with PHPUnit or Pest and PHPStan before merging to catch framework-specific regressions Rector cannot detect alone.

Rector only changes files you explicitly apply after reviewing dry-run output. Risk comes from skipping tests, enabling aggressive type-declaration sets on untyped legacy code, or deploying without staging validation. Nullable return types on legacy methods and dynamic property access sometimes need human review because Rector cannot infer intent when multiple refactor paths exist. Always run PHPUnit or Pest, PHPStan at your agreed level, deploy to staging first, and smoke-test critical flows before production. Never run a blind Rector pass directly on production.

They solve different problems and work best together. Rector rewrites code by applying AST transformation rules. PHPStan and Psalm analyse code without changing files. In practice I run Rector to modernise syntax, PHPStan to validate type correctness, and Laravel Pint or PHP-CS-Fixer to format the result. Rector adds types where it can infer them safely; PHPStan proves those types hold. Neither tool replaces your test suite for business logic guarantees.

A well-tested Laravel app with 500 to 2,000 PHP files often needs one to three days of Rector runs plus about a week of review and QA. A 50,000-line Laravel app might require three to five phased PRs over two sprints. Timeline grows with missing tests, custom framework wrappers, and excluded legacy directories. That phased approach still beats a six-month manual rewrite costing Rs 400,000 to 800,000 (~USD 3,000 to 6,000) in contractor time for Nepal-based teams.

Create rector.php at the project root with paths pointing to app, routes, and src directories while skipping vendor, storage, and bootstrap/cache. Add rector/rector-laravel via Composer, import LaravelLevelSetList and LaravelSetList, and enable UP_TO_LARAVEL_120 plus LARAVEL_CODE_QUALITY after PHP level sets. Exclude generated files, cached views, and migration stubs you plan to squash using skip() or a rector-baseline.php. Run dry-run first, split diffs by directory, and validate with your test suite before merge.

Yes, CI integration prevents drift. The recommended pattern gates merges on dry-run failure: if Rector would change any file, the job exits non-zero and blocks the merge. A GitLab CI job running vendor/bin/rector process --dry-run --no-progress-bar on merge requests and main works well. Cache vendor between jobs for runs under two minutes on typical Laravel apps. Optionally add a pre-commit hook that fails when pending Rector changes exist. For Deployer 7 pipelines, run Rector on staging before production, never blind on live.

Use Rector for repetitive syntactic changes across many files: PHP 8.2 to 8.5 upgrades, removing deprecated dynamic properties, constructor property promotion, Laravel framework renames after minor version bumps, and normalising legacy WordPress plugin code. Stick to manual refactors when redesigning service boundaries, changing database schema with Eloquent relationships, rewriting payment gateway callback idempotency, or restructuring legacy CodeIgniter routing. On Nepal Gift Card, Rector handled PHP syntax while we manually rewrote order-state logic.

Always exclude vendor, storage, bootstrap/cache, generated files, cached Blade views, and migration stubs you plan to squash. Use skip() in rector.php or commit a rector-baseline.php for directories under active rewrite. For WordPress and WooCommerce 11.1 projects, scope Rector runs to custom plugins only—never point it at wp-core or third-party vendor plugin trees. Dynamic $this->$property access in skipped files may silently miss rules, so review excluded paths periodically as you modernise legacy modules.

Rector cannot infer intent when two refactor paths exist, so nullable return types on legacy methods often need human review. Dynamic property access may skip rules silently. Enabling type-declaration sets before PHPStan passes on your current baseline produces noisy diffs. After upgrading, tune OPcache and verify Composer autoloader settings because new class layouts can shift autoload performance. Read official PHP 8.5 migration notes on php.net for deprecations Rector level sets have not codified yet. Pair every Rector pass with static analysis and staging deploys.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: