Skip to content

Latest commit

 

History

History
102 lines (75 loc) · 4.16 KB

how_it_works.md

File metadata and controls

102 lines (75 loc) · 4.16 KB

How Does Rector Work?

(Inspired by How it works in BetterReflection)

1. Finds all files and Load Configured Rectors

  • The application finds files in the source code you provide and registered Rectors - from --config or local rector.php
  • Then it iterates all found files and applies relevant Rectors to them.
  • A Rector in this context is 1 single class that modifies 1 thing, e.g. changes the class name

2. Parse and Reconstruct 1 File

The iteration of files, nodes and Rectors respects this lifecycle:

<?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);
                }
            }
        }
    }
}

2.1 Prepare Phase

  • Files are 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 StandaloneTraverseNodeTraverser to prepare their metadata, e.g. the class name, the method node the node is in, the namespace name etc. added by $node->setAttribute(Attribute::CLASS_NODE, 'value').

2.2 Rectify Phase

  • When all nodes are ready, the application iterates on all active Rectors
  • Each node is compared with $rector->getNodeTypes() method to see if this Rector should do some work on it, e.g. is this class name called OldClassName?
  • If it doesn't match, it goes to next node.
  • If it matches, the $rector->reconstruct($node) method is called
  • Active Rector change everything they have to and return changed nodes

2.2.1 Order of Rectors

  • Rectors are run by they natural order in the configuration, meaning the first in the configuration will be run first.

E.g. in this case, first the @expectedException annotation will be changed to a method, then the setExpectedException method will be changed to expectedException.

<?php
// rector.php

declare(strict_types=1);

use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator): void {
    $services = $containerConfigurator->services();
    $services->set(Rector\PHPUnit\Rector\ClassMethod\ExceptionAnnotationRector::class);
    $services->set(Rector\Renaming\Rector\MethodCall\RenameMethodRector::class)
        ->arg('$oldToNewMethodsByClass', [
             PHPUnit\Framework\TestClass::class => [
                'setExpectedException' => 'expectedException',
                'setExpectedExceptionRegExp' => 'expectedException',
            ],
        ]);
};

2.3 Save File/Diff Phase

  • When work on all nodes of 1 file is done, the file will be saved if it has some changes
  • Or if the --dry-run option is on, it will store the git-like diff thanks to GeckoPackages/GeckoDiffOutputBuilder
  • Then Rector will go to the next file

3 Reporting

  • After this, Rector displays the list of changed files
  • Or with --dry-run option the diff of these files

Similar Projects