rector/rector-generator
Rector Generator scaffolds custom Rector rules from templates, helping you create PHP refactoring and upgrade automation faster with ready-made rule classes and supporting files.
Deprecation notice:
rector/rector-generatorlives under thedeprecated-packagesorg and is archived. The rule-scaffolding workflow it pioneered was folded into Rector core as thegeneratecommand. If you are on a modern Rector (1.x), skip this package and runvendor/bin/rector generatefromrector/rectordirectly. Reach forrector-generatoronly if you are pinned to an older Rector that predates the built-in command.
Minimal steps to get a working scaffold:
composer require --dev rector/rector-generator.rector.php config exists at project root – the generator reads paths/namespaces from it and from composer.json autoloading.vendor/bin/rector generate (older builds may expose it as a standalone binary; check vendor/bin/ and the README for your pinned version).The command produces a triplet you will live in every time you write a rule:
rules/.../YourRector.php – the Rector rule skeleton.rules-tests/.../YourRector/YourRectorTest.php – a PHPUnit test extending AbstractRectorTestCase.rules-tests/.../YourRector/Fixture/*.php.inc – input/expected fixture pair.First use case: scaffold a rule that rewrites a legacy helper call (old_helper()) into a new API (NewHelper::run()), then fill in getRuleDefinition(), getNodeTypes() and refactor(). Run vendor/bin/rector process path/to/fixture --dry-run to watch it rewrite. The generated test is your regression harness from the very first commit.
Fixture-first loop. Write the input fixture by hand (the code you want changed), scaffold the rule, then implement refactor() until vendor/bin/rector process ... --dry-run emits the expected output. Copy the diff into the expected file and lock it in with vendor/bin/phpunit rules-tests/.../YourRectorTest.php.
One rule, one node type. getNodeTypes() should return the narrowest node class that can express your change (MethodCall, ClassMethod, PropertyFetch). The generator's skeleton often defaults to a broader node – tighten it immediately so Rector's traversal doesn't invoke your rule on unrelated AST nodes.
Keep getRuleDefinition() honest. The code sample in getRuleDefinition() is rendered in Rector's docs and error messages. Stale samples are the most common review comment on generated rules; update them whenever refactor() semantics change.
Registration in rector.php. The generator usually appends the new rule to withRules([...]), but verify. During development you can also register the single rule inline and scope it with withPaths() to keep iteration fast instead of running the whole ruleset.
Test harness contract. The generated test almost always boils down to:
protected function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
That config file must instantiate the rule. If the generator did not create it, create it – a missing config is the usual reason a freshly generated test reports "0 assertions".
Batch scaffolding. When migrating a large codebase, you will write many small, near-identical rules (e.g. one per deprecated method). Generate the first one, then copy the triplet and adjust the prompts' outputs rather than re-running the interactive command dozens of times. This preserves your project's exact naming and path conventions.
CI integration. Add a job that runs vendor/bin/phpunit rules-tests alongside vendor/bin/rector process --dry-run on the repo itself. Generated rules that rewrite your own code are cheap to validate and catch regressions early.
rector-generator targets older Rector internals (AbstractRector, RectorRecipe, older node-type contracts). Pinning Rector to a matching minor is essential; a Composer update can break the generator and the rules it produced. Prefer core rector generate wherever possible.RectorConfig->withAutoloadPaths() or the test will fail with "class not found" rather than a rule error.input/expected file pairs (often with a .php.inc extension) under a Fixture/ directory. Renaming a file without updating the test silently drops coverage.vendor/bin/rector clear-cache (or --clear-cache) before concluding the rule is broken.--dry-run plus -v. --dry-run shows the would-be diff; -v/-vv prints the node traversal details that tell you whether refactor() was even entered.refactor() can return a new node, an array of nodes, or null. Returning null from a branch silently means "no change" – a frequent cause of "my rule does nothing" reports. Be explicit about which contract you are using for your Rector version.refactor() with $node instanceof ... even when getNodeTypes() is narrow; subclasses and dynamic calls can surprise you.tools/ script instead and freeze the template.composer.json.How can I help you explore Laravel packages today?