(Inspired by How it works in BetterReflection)
- The application finds files in source you provide and registeres Rectors - from
--level,--configor localrector.yaml - Then it iterates all found files and applies relevant Rectors to them.
- Rector in this context is 1 single class that modifies 1 thing, e.g. changes class name
The iteration of files, nodes and Rectors respects this life cycle:
<?php declare(strict_types=1);
use Rector\Contract\Rector\PhpRectorInterface;
use PhpParser\Parser;
/** @var SplFileInfo[] $fileInfos */
foreach ($fileInfos as $fileInfo) {
// 1 file => nodes
/** @var Parser $phpParser */
$nodes = $phpParser->parse(file_get_contents($fileInfo->getRealPath()));
// nodes => 1 node
foreach ($nodes as $node) { // rather traverse all of them
/** @var PhpRectorInterface[] $rectors */
foreach ($rectors as $rector) {
foreach ($rector->getNodeTypes() as $nodeType) {
if (is_a($node, $nodeType, true)) {
$rector->refactor($node);
}
}
}
}
}- File is parsed by
nikic/php-parser, 4.0 that supports writing modified tree back to a file - Then nodes (array of objects by parser) are traversed by
StandaloneTraverseNodeTraverserto prepare it's metadata, e.g. class name, method node the node is in, namespace name etc. added by$node->setAttribute(Attribute::CLASS_NODE, 'value').
- When all nodes are ready, applicies iterates all active Rector
- Each nodes is compared to
$rector->getNodeTypes()method to see, if this Rector should do some work on it, e.g. is this class name calledOldClassName? - If it doesn't match, it goes to next node.
- If it matches, the
$rector->reconstruct($node)method is called - Active Rector changes all what he should and returns changed node
- Rectors are run by they natural order in config, first will be run first.
E.g. in this case, first will be changed @expectedException annotation to method,
then a method setExpectedException to expectedException.
# rector.yaml
services:
Rector\PHPUnit\Rector\ExceptionAnnotationRector: ~
Rector\Rector\MethodCall\RenameMethodRector:
$perClassOldToNewMethods:
'PHPUnit\Framework\TestClass':
'setExpectedException': 'expectedException'
'setExpectedExceptionRegExp': 'expectedException'- When work on all nodes of 1 file is done, the file will be saved if it has some changes
- Or if the
--dry-runoption is on, it will store the git-like diff thanks to GeckoPackages/GeckoDiffOutputBuilder - Then go to next file
- After this Rector displays list of changed files
- Or with
--dry-runoption the diff of these files
- ClangMR for C++ by Google (closed source) - almost idential workflow, developed independently though
- facebook/jscodeshift for Javascript
- silverstripe/silverstripe-upgrader for PHP CMS, Silverstripe
- dereuromark/upgrade for PHP Framework, CakePHP