Updated Rector to commit 25821f6212

25821f6212 [TASK] Add documentation about FileProcessors (#121)
This commit is contained in:
Tomas Votruba 2021-06-13 11:31:37 +00:00
parent ab3b6b2c9e
commit d39c210590
8 changed files with 328 additions and 20 deletions

View File

@ -39,6 +39,7 @@ By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring
- [How to Ignore Rule or Paths](/docs/how_to_ignore_rule_or_paths.md)
- [Static Reflection and Autoload](/docs/static_reflection_and_autoload.md)
- [How to Configure Rule](/docs/how_to_configure_rules.md)
- [Beyond PHP - Entering the realm of FileProcessors](/docs/beyond_php_file_processors.md)
### For Rule Developers and Contributors
@ -47,7 +48,7 @@ By [buying a book](https://leanpub.com/rector-the-power-of-automated-refactoring
- [How to Work with Doc Block and Comments](/docs/how_to_work_with_doc_block_and_comments.md)
- [How to Generate New Rector Rule](/docs/create_own_rule.md)
- [How to add Test for Rector Rule](/docs/how_to_add_test_for_rector_rule.md)
- [How to create a custom FileProcessor](/docs/create_custom_fileprocessor.md)
<br>
## Install

View File

@ -0,0 +1,101 @@
# Beyond PHP - FileProcessors
You think Rector is all about PHP? You might be wrong.
Sure, the vast majority of the rules included in Rector is for PHP-Code. That´s true.
But since version 0.11.x Rector introduced the concept of so called FileProcessors.
When you are running Rector with the process command all collected files from your configured paths
are iterated through all registered FileProcessors.
Each FileProcessor must implement the [FileProcessorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php) and must decide if it is able to handle a given file by
the [supports](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Processor/FileProcessorInterface.php#L11) method or not.
Rector itself already ships with three FileProcessors. Whereas the most important one, you guessed it, is the PhpFileProcessor.
But another nice one is the ComposerFileProcessor. The ComposerFileProcessor lets you manipulate composer.json files in your project.
Let´s say you want to define a custom configuration where you want to update the version constraint of some packages.
All you have to do is using the ChangePackageVersionComposerRector:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Composer\Rector\ChangePackageVersionComposerRector;
use Rector\Composer\ValueObject\PackageAndVersion;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(ChangePackageVersionComposerRector::class)
->call('configure', [[
// we use constant for keys to save you from typos
ChangePackageVersionComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
new PackageAndVersion('symfony/yaml', '^5.0'),
]),
]]);
};
```
There are some more rules related to manipulate your composer.json files. Let´s see them in action:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Composer\Rector\AddPackageToRequireComposerRector;
use Rector\Composer\Rector\AddPackageToRequireDevComposerRector;
use Rector\Composer\Rector\RemovePackageComposerRector;
use Rector\Composer\Rector\ReplacePackageAndVersionComposerRector;
use Rector\Composer\ValueObject\PackageAndVersion;
use Rector\Composer\ValueObject\ReplacePackageAndVersion;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Symplify\SymfonyPhpConfig\ValueObjectInliner;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
// Add a package to the require section of your composer.json
$services->set(AddPackageToRequireComposerRector::class)
->call('configure', [[
// we use constant for keys to save you from typos
AddPackageToRequireComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
new PackageAndVersion('symfony/yaml', '^5.0'),
]),
]]);
// Add a package to the require dev section of your composer.json
$services->set(AddPackageToRequireDevComposerRector::class)
->call('configure', [[
// we use constant for keys to save you from typos
AddPackageToRequireDevComposerRector::PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
new PackageAndVersion('phpunit/phpunit', '^9.0'),
]),
]]);
// Remove a package from composer.json
$services->set(RemovePackageComposerRector::class)
->call('configure', [[
// we use constant for keys to save you from typos
RemovePackageComposerRector::PACKAGE_NAMES => ['symfony/console']
]]);
// Replace a package in the composer.json
$services->set(ReplacePackageAndVersionComposerRector::class)
->call('configure', [[
// we use constant for keys to save you from typos
ReplacePackageAndVersionComposerRector::REPLACE_PACKAGES_AND_VERSIONS => ValueObjectInliner::inline([
new ReplacePackageAndVersion('vendor1/package2', 'vendor2/package1', '^3.0'),
]),
]]);
};
```
Behind every FileProcessor are one or multiple rules which are in turn implementing a dedicated Interface extending the [RectorInterface](https://github.com/rectorphp/rector-src/blob/main/src/Contract/Rector/RectorInterface.php).
In the case of the ComposerFileProcessor all rules are implementing the [ComposerRectorInterface](https://github.com/rectorphp/rector-src/blob/main/rules/Composer/Contract/Rector/ComposerRectorInterface.php)
Are you eager to create your own one? Dive in and have a look at [How to create a custom FileProcessor](how_to_create_custom_fileprocessor.md)

View File

@ -0,0 +1,206 @@
# Create your own custom FileProcessor
This section is all about creating your custom specific FileProcessor.
If you don´t know the concept of FileProcessors in the context of Rector, have a look at [Beyond PHP - Entering the realm of FileProcessors](beyond_php_file_processors.md)
Most of the examples starting with a rather contrived example, let´s do it the same.
Imagine you would like to replace the sentence "Make america great again" to "Make the whole world a better place to be" in every file named bold_statement.txt.
In order to do so, we create the BoldStatementFileProcessor like that:
```php
<?php
namespace MyVendor\MyPackage\FileProcessor;
use Rector\Core\Contract\Processor\FileProcessorInterface;
use Rector\Core\ValueObject\Application\File;
final class BoldStatementFileProcessor implements FileProcessorInterface
{
/**
* @var string
*/
private const OLD_STATEMENT = 'Make america great again';
public function supports(File $file): bool
{
$smartFileInfo = $file->getSmartFileInfo();
return 'bold_statement.txt' === $smartFileInfo->getBasename();
}
/**
* @param File[] $files
*/
public function process(array $files): void
{
foreach ($files as $file) {
$this->processFile($file);
}
}
private function processFile(File $file): void
{
$oldContent = $file->getFileContent();
if(false === strpos($oldContent, self::OLD_STATEMENT)) {
return;
}
$newFileContent = str_replace(self::OLD_STATEMENT, 'Make the whole world a better place to be', $oldContent);
$file->changeFileContent($newFileContent);
}
public function getSupportedFileExtensions(): array
{
return ['txt'];
}
}
```
Now register your FileProcessor in your configuration (actually in the container):
```php
<?php
// rector.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use MyVendor\MyPackage\FileProcessor\BoldStatementFileProcessor;
return static function (ContainerConfigurator $containerConfigurator): void {
// [...]
$services = $containerConfigurator->services();
$services->set(BoldStatementFileProcessor::class);
};
```
Run rector again and see what happens. Yes, we made the world better.
The astute reader has noticed, that the BoldStatementFileProcessor is not really reusable and easily extendable.
So it would be much better to separate the processing from the actual rule(s).
This is also the best practice in all Rector internal FileProcessors. So, let´s just do that.
Create a new dedicated Interface for our rules used by the BoldStatementFileProcessor. Just call it BoldStatementRectorInterface.
```php
<?php
namespace MyVendor\MyPackage\FileProcessor\Rector;
use Rector\Core\Contract\Rector\RectorInterface;
interface BoldStatementRectorInterface extends RectorInterface
{
public function transform(string $content): string;
}
```
Now, separate the modification from the processing:
```php
<?php
namespace MyVendor\MyPackage\FileProcessor\Rector;
use Rector\Core\ValueObject\Application\File;
final class BoldStatementMakeAmericaGreatAgainRector implements BoldStatementRectorInterface
{
/**
* @var string
*/
private const OLD_STATEMENT = 'Make america great again';
public function transform(string $content): string
{
if(false === strpos($content, self::OLD_STATEMENT)) {
return;
}
return str_replace(self::OLD_STATEMENT, 'Make the whole world a better place to be', $content);
}
}
```
And change our BoldStatementFileProcessor so it is using one or multiple classes implementing the BoldStatementRectorInterface:
```php
<?php
namespace MyVendor\MyPackage\FileProcessor;
use Rector\Core\Contract\Processor\FileProcessorInterface;
use Rector\Core\ValueObject\Application\File;
use MyVendor\MyPackage\FileProcessor\Rector\BoldStatementRectorInterface;
final class BoldStatementFileProcessor implements FileProcessorInterface
{
/**
* @var BoldStatementRectorInterface[]
*/
private $boldStatementRectors;
/**
* @param BoldStatementRectorInterface[] $boldStatementRectors
*/
public function __construct(array $boldStatementRectors)
{
$this->boldStatementRectors = $boldStatementRectors;
}
public function supports(File $file): bool
{
$smartFileInfo = $file->getSmartFileInfo();
return 'bold_statement.txt' === $smartFileInfo->getBasename();
}
/**
* @param File[] $files
*/
public function process(array $files): void
{
foreach ($files as $file) {
$this->processFile($file);
}
}
private function processFile(File $file): void
{
foreach ($this->boldStatementRectors as $boldStatementRector) {
$changeFileContent = $boldStatementRector->transform($file->getFileContent());
$file->changeFileContent($changeFileContent);
}
}
public function getSupportedFileExtensions(): array
{
return ['txt'];
}
}
```
Notice the annotation BoldStatementRectorInterface[]. This is important to inject all active classes implementing the BoldStatementRectorInterface into the BoldStatementFileProcessor.
Yes, we said active. So last but not least we must register our new rule in the container, so it is applied:
```php
<?php
// rector.php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use MyVendor\MyPackage\FileProcessor\BoldStatementFileProcessor;
use MyVendor\MyPackage\FileProcessor\Rector\BoldStatementMakeAmericaGreatAgainRector;
return static function (ContainerConfigurator $containerConfigurator): void {
// [...]
$services = $containerConfigurator->services();
$services->set(BoldStatementFileProcessor::class);
$services->set(BoldStatementMakeAmericaGreatAgainRector::class);
};
```
Run rector again and yes, we made the world a better place again.
Puh. This was a long ride. But we are done and have our new shiny BoldStatementFileProcessor in place.
Now, it´s up to you, to create something useful. But always keep in mind: Try to make the world a better place to be.

View File

@ -16,11 +16,11 @@ final class VersionResolver
/**
* @var string
*/
public const PACKAGE_VERSION = '651562dca23b2637184260a754f4dcdd7c41c520';
public const PACKAGE_VERSION = '25821f62122d0b1f42080389045a542dfe2343d3';
/**
* @var string
*/
public const RELEASE_DATE = '2021-06-13 11:47:20';
public const RELEASE_DATE = '2021-06-13 13:19:16';
public static function resolvePackageVersion() : string
{
$process = new \RectorPrefix20210613\Symfony\Component\Process\Process(['git', 'log', '--pretty="%H"', '-n1', 'HEAD'], __DIR__);

2
vendor/autoload.php vendored
View File

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969::getLoader();
return ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc::getLoader();

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
class ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc
{
private static $loader;
@ -22,15 +22,15 @@ class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::getInitializer($loader));
} else {
$classMap = require __DIR__ . '/autoload_classmap.php';
if ($classMap) {
@ -42,19 +42,19 @@ class ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969
$loader->register(true);
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$files;
$includeFiles = Composer\Autoload\ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequirea07e25d44a859f4e8c5e186c56ba3969($fileIdentifier, $file);
composerRequireebaae27a38f113f52c90b5eba352fcbc($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequirea07e25d44a859f4e8c5e186c56ba3969($fileIdentifier, $file)
function composerRequireebaae27a38f113f52c90b5eba352fcbc($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969
class ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc
{
public static $files = array (
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
@ -3863,9 +3863,9 @@ class ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInita07e25d44a859f4e8c5e186c56ba3969::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInitebaae27a38f113f52c90b5eba352fcbc::$classMap;
}, null, ClassLoader::class);
}

View File

@ -21,8 +21,8 @@ if (!class_exists('SomeTestCase', false) && !interface_exists('SomeTestCase', fa
if (!class_exists('CheckoutEntityFactory', false) && !interface_exists('CheckoutEntityFactory', false) && !trait_exists('CheckoutEntityFactory', false)) {
spl_autoload_call('RectorPrefix20210613\CheckoutEntityFactory');
}
if (!class_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false) && !interface_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false) && !trait_exists('ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969', false)) {
spl_autoload_call('RectorPrefix20210613\ComposerAutoloaderInita07e25d44a859f4e8c5e186c56ba3969');
if (!class_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false) && !interface_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false) && !trait_exists('ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc', false)) {
spl_autoload_call('RectorPrefix20210613\ComposerAutoloaderInitebaae27a38f113f52c90b5eba352fcbc');
}
if (!class_exists('Doctrine\Inflector\Inflector', false) && !interface_exists('Doctrine\Inflector\Inflector', false) && !trait_exists('Doctrine\Inflector\Inflector', false)) {
spl_autoload_call('RectorPrefix20210613\Doctrine\Inflector\Inflector');
@ -3323,9 +3323,9 @@ if (!function_exists('print_node')) {
return \RectorPrefix20210613\print_node(...func_get_args());
}
}
if (!function_exists('composerRequirea07e25d44a859f4e8c5e186c56ba3969')) {
function composerRequirea07e25d44a859f4e8c5e186c56ba3969() {
return \RectorPrefix20210613\composerRequirea07e25d44a859f4e8c5e186c56ba3969(...func_get_args());
if (!function_exists('composerRequireebaae27a38f113f52c90b5eba352fcbc')) {
function composerRequireebaae27a38f113f52c90b5eba352fcbc() {
return \RectorPrefix20210613\composerRequireebaae27a38f113f52c90b5eba352fcbc(...func_get_args());
}
}
if (!function_exists('parseArgs')) {