README + recipe improvements (#4017)

* rector-recipe and README improvements

* ci: add generate command test
This commit is contained in:
Tomas Votruba 2020-08-24 23:25:26 +02:00 committed by GitHub
parent 642cda46bd
commit de4748f935
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 416 additions and 841 deletions

View File

@ -0,0 +1,34 @@
name: Code Analysis [no dev]
on:
pull_request: null
push:
branches:
- master
jobs:
matrix:
strategy:
fail-fast: false
matrix:
actions:
-
name: 'Rector Recipe'
run: |
cp rector-recipe.php.dist rector-recipe.php
bin/rector generate
name: ${{ matrix.actions.name }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# see https://github.com/shivammathur/setup-php
- uses: shivammathur/setup-php@v1
with:
php-version: 7.4
coverage: none
- run: composer install --no-progress --ansi
- run: ${{ matrix.actions.run }}

381
README.md
View File

@ -64,18 +64,14 @@ It supports all versions of PHP from 5.2 and many open-source projects:
## What Can Rector Do for You?
- [Upgrade 30 000 unit tests from PHPUnit 6 to 9 in 2 weeks](https://twitter.com/LBajsarowicz/status/1272947900016967683)
- Complete [@var annotations or parameter/return type declarations](https://www.tomasvotruba.com/blog/2019/01/03/how-to-complete-type-declarations-without-docblocks-with-rector/)
- [Complete PHP 7.4 property type declarations](https://www.tomasvotruba.com/blog/2018/11/15/how-to-get-php-74-typed-properties-to-your-code-in-few-seconds/)
- Complete [@var annotations or parameter/return type declarations](https://tomasvotruba.com/blog/2019/01/03/how-to-complete-type-declarations-without-docblocks-with-rector/)
- [Complete PHP 7.4 property type declarations](https://tomasvotruba.com/blog/2018/11/15/how-to-get-php-74-typed-properties-to-your-code-in-few-seconds/)
- Upgrade your code from **PHP 5.3 to 8.0**
- [Migrate your project from Nette to Symfony](https://www.tomasvotruba.com/blog/2019/02/21/how-we-migrated-from-nette-to-symfony-in-3-weeks-part-1/)
- [Refactor Laravel facades to dependency injection](https://www.tomasvotruba.com/blog/2019/03/04/how-to-turn-laravel-from-static-to-dependency-injection-in-one-day/)
- [Migrate your project from Nette to Symfony](https://tomasvotruba.com/blog/2019/02/21/how-we-migrated-from-nette-to-symfony-in-3-weeks-part-1/)
- [Refactor Laravel facades to dependency injection](https://tomasvotruba.com/blog/2019/03/04/how-to-turn-laravel-from-static-to-dependency-injection-in-one-day/)
- And much more...
## How to Apply Coding Standards?
Rector uses [nikic/php-parser](https://github.com/nikic/PHP-Parser/), that build on technology called *abstract syntax tree* (AST). AST doesn't care about spaces and produces mall-formatted code. That's why your project needs to have coding standard tool and set of rules, so it can make refactored nice and shiny again.
Don't have any coding standard tool? Add [EasyCodingStandard](https://github.com/Symplify/EasyCodingStandard) and use prepared [`ecs-after-rector.php`](/ecs-after-rector.php) set.
<br>
## Install
@ -86,160 +82,68 @@ composer require rector/rector --dev
- Having conflicts during `composer require`? → Use the [Rector Prefixed](https://github.com/rectorphp/rector-prefixed)
- Using a different PHP version than Rector supports? → Use the [Docker image](#run-rector-in-docker)
<br>
## Running Rector
### A. Prepared Sets
There a 2 main ways to use Rector:
Featured open-source projects have **prepared sets**. You can find them in [`/config/set`](/config/set) or by autocomplete of [`Rector\Set\ValueObject\SetList`](/packages/set/src/ValueObject/SetList.php) constants in `rector.php` config.
- a *single rule*, to have the change under control - pick [from over 550 rules](/docs/rector_rules_overview.md)
- or group of rules called *sets* - pick from [sets](/config/set)
Let's say you pick the [`symfony40`](/config/set/symfony40.php) set and you want to upgrade your `/src` directory:
Sets are suitable for open-source projects and design patterns, like .
To use them, create a `rector.php` in your root directory:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Php74\Rector\Property\TypedPropertyRector;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
// get parameters
$parameters = $containerConfigurator->parameters();
// here we can define, what sets of rules will be applied
$parameters->set(Option::SETS, [
SetList::CODE_QUALITY
]);
// get services
$services = $containerConfigurator->services();
// register single rule
$services->set(TypedPropertyRector::class);
};
```
<br>
Then dry run Rector:
```bash
vendor/bin/rector process src --set symfony40 --dry-run
vendor/bin/rector process src --dry-run
```
Rector will show you diff of files that it *would* change. To *make* the changes, drop `--dry-run`:
```bash
# apply upgrades to your code
vendor/bin/rector process src --set symfony40
```
Some sets, such as [`code-quality`](/config/set/code-quality.php) can be used on a regular basis. **The best practice is to use config over command line**:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [SetList::CODE_QUALITY]);
};
```
PHP config format is a new [Symfony best practice](https://twitter.com/symfony_en/status/1284538366147678208).
### B. Standalone Rules
In the end, it's best to combine few of basic sets and drop [particular rules](/docs/rector_rules_overview.md) that you want to try:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use Rector\Php74\Rector\Property\TypedPropertyRector;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$services = $containerConfigurator->services();
$services->set(TypedPropertyRector::class);
$parameters->set(Option::SETS, [SetList::CODE_QUALITY]);
};
```
Then let Rector refactor your code:
```bash
vendor/bin/rector process src
```
:+1:
<br>
*Note: `rector.php` is loaded by default. For different location, use `--config` option.*
## Features
<br>
### Paths
If you're annoyed by repeating paths in arguments, you can move them to config instead:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PATHS, [
__DIR__ . '/src',
__DIR__ . '/tests',
]);
};
```
### Extra Autoloading
Rector relies on whatever autoload setup the project it is fixing is using by using the Composer autoloader as default. To specify your own autoload file, use `--autoload-file` option:
```bash
vendor/bin/rector process ../project --autoload-file ../project/vendor/autoload.php
```
Or use a `rector.php` configuration file:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::AUTOLOAD_PATHS, [
__DIR__ . '/vendor/squizlabs/php_codesniffer/autoload.php',
__DIR__ . '/vendor/project-without-composer',
]);
};
```
### Exclude Paths and Rectors
You can also **exclude files or directories** (with regex or [fnmatch](http://php.net/manual/en/function.fnmatch.php)):
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::EXCLUDE_PATHS, [
__DIR__ . '/src/*/Tests/*',
]);
};
```
You can use a whole set, except 1 rule:
## Configuration
```php
<?php
@ -249,29 +153,66 @@ declare(strict_types=1);
use Rector\CodeQuality\Rector\If_\SimplifyIfReturnBoolRector;
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [
SetList::CODE_QUALITY,
// paths to refactor; solid alternative to CLI arguments
$parameters->set(Option::PATHS, [
__DIR__ . '/src',
__DIR__ . '/tests',
]);
// is there a file you need to skip?
$parameters->set(Option::EXCLUDE_PATHS, [
// single file
__DIR__ . '/src/ComplicatedFile.php',
// or directory
__DIR__ . '/src/ComplicatedFile.php',
// or fnmatch
__DIR__ . '/src/*/Tests/*',
]);
// Rector relies on autoload setup of your project; Composer autoload is included by default; to add more:
$parameters->set(Option::AUTOLOAD_PATHS, [
// autoload specific file
__DIR__ . '/vendor/squizlabs/php_codesniffer/autoload.php',
// or full directory
__DIR__ . '/vendor/project-without-composer',
]);
// is there single rule you don't like from a set you use?
$parameters->set(Option::EXCLUDE_RECTORS, [
SimplifyIfReturnBoolRector::class,
]);
// is your PHP version different from the one your refactor to? [default: your PHP version]
$parameters->set(Option::PHP_VERSION_FEATURES, '7.2');
// auto import fully qualified class names? [default: false]
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
// skip root namespace classes, like \DateTime or \Exception [default: true]
$parameters->set(Option::IMPORT_SHORT_CLASSES, false);
// skip classes used in PHP DocBlocks, like in /** @var \Some\Class */ [default: true]
$parameters->set(Option::IMPORT_DOC_BLOCKS, false);
};
```
### Ignore Rector Rule in File
For in-file exclusion, use `@noRector \FQN name` annotation:
```php
class SomeClass
{
/**
* @noRector \Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector
* @noRector
*/
const NAME = '102';
/**
* @noRector
*/
public function foo()
{
@ -287,39 +228,17 @@ Do you have config that includes many sets and Rectors? You might want to run on
```bash
vendor/bin/rector process src --set solid --only Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector
```
Or just short name:
```bash
# or just a short class name
vendor/bin/rector process src --set solid --only FinalizeClassesWithoutChildrenRector
```
Both will run only `Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector`.
### Provide PHP Version
By default Rector uses the language features matching your system version of PHP. You can configure it for a different PHP version:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::PHP_VERSION_FEATURES, '7.2'); # your version is 7.3
};
```
### Safe Types
In default setting:
**Experimental** feature
In default type resolving settings, all docblocks are taken seriously.
```php
<?php
@ -333,19 +252,19 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SAFE_TYPES, false);
// [default: false]
$parameters->set(Option::SAFE_TYPES, true);
};
```
All docblocks are taken seriously, e.g. with [typed properties](https://github.com/rectorphp/rector/blob/master/docs/rector_rules_overview.md#typedpropertyrector) rule:
E.g. the `TypedPropertyRector` rule will skip this case, as `string` is defined only in docblock:
```diff
<?php
```php
<?php
class ValueObject
{
- public $value;
+ public string $value;
class ValueObject
{
public $value;
/**
* @param string $value
@ -357,88 +276,6 @@ All docblocks are taken seriously, e.g. with [typed properties](https://github.c
}
```
Do you want to use only explicit PHP type declaration? Enable `safe_types`:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SAFE_TYPES, true);
};
```
Then, docblocks are skipped:
```diff
<?php
class ValueObject
{
public $value;
- public $count;
+ public int $count;
/**
* @param string $value
*/
public function __construct($value, int $count)
{
$this->value = $value;
$this->count = $count
}
}
```
### Import Use Statements
FQN classes are not imported by default. If you don't want to do it manually after every Rector run, enable it by:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
};
```
You can also fine-tune how these imports are processed:
```php
<?php
// rector.php
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
// this will not import root namespace classes, like \DateTime or \Exception
$parameters->set(Option::IMPORT_SHORT_CLASSES, false);
// this will not import classes used in PHP DocBlocks, like in /** @var \Some\Class */
$parameters->set(Option::IMPORT_DOC_BLOCKS, false);
};
```
### Limit Execution to Changed Files
Execution can be limited to changed files using the `process` option `--match-git-diff`.
@ -503,9 +340,9 @@ Using `rector.php`:
```bash
docker run --rm -v $(pwd):/project rector/rector:latest process /project/app \
--config /project/rector.php \
--autoload-file /project/vendor/autoload.php \
--dry-run
--config /project/rector.php \
--autoload-file /project/vendor/autoload.php \
--dry-run
```
<br>
@ -525,3 +362,11 @@ Do you use Rector to upgrade your code? Add it here:
- [palantirnet/drupal-rector](https://github.com/palantirnet/drupal-rector) by [Palantir.net](https://github.com/palantirnet) for [Drupal](https://www.drupal.org/)
- [sabbelasichon/typo3-rector](https://github.com/sabbelasichon/typo3-rector) for [TYPO3](https://typo3.org/)
## Known Drawbacks
### How to Apply Coding Standards?
Rector uses [nikic/php-parser](https://github.com/nikic/PHP-Parser/), that build on technology called *abstract syntax tree* (AST). AST doesn't care about spaces and produces mall-formatted code in both PHP and docblock annotations. **That's why your project needs to have coding standard tool** and set of rules, so it can make refactored nice and shiny again.
Don't have any coding standard tool? Add [ECS](https://github.com/Symplify/EasyCodingStandard) and use prepared [`ecs-after-rector.php`](/ecs-after-rector.php) set.

View File

@ -16,7 +16,7 @@
"doctrine/annotations": "^1.10.4",
"doctrine/inflector": "^1.4|^2.0",
"jean85/pretty-package-versions": "^1.2",
"migrify/php-config-printer": "^0.3.30",
"migrify/php-config-printer": "^0.3.31",
"nette/robot-loader": "^3.2",
"nette/utils": "^3.1",
"nikic/php-parser": "^4.9",
@ -43,8 +43,8 @@
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.16",
"johnkary/phpunit-speedtrap": "^3.2",
"migrify/config-transformer": "^0.3.30",
"migrify/easy-ci": "^0.3.30",
"migrify/config-transformer": "^0.3.31",
"migrify/easy-ci": "^0.3.31",
"nette/application": "^3.0",
"nette/forms": "^3.0",
"ocramius/package-versions": "^1.4|^1.5",

View File

@ -11,7 +11,7 @@ use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector;
use Rector\Renaming\ValueObject\StaticCallRename;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Rector\TypeDeclaration\Rector\ClassMethod\AddParamTypeDeclarationRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -77,7 +77,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(RenameStaticMethodRector::class)
->call('configure', [[
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_objects($configuration),
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_value_objects($configuration),
]]);
$services->set(RenamePropertyRector::class)

View File

@ -9,7 +9,7 @@ use Rector\Generic\ValueObject\FuncNameToMethodCallName;
use Rector\Guzzle\Rector\MethodCall\MessageAsArrayRector;
use Rector\MagicDisclosure\Rector\MethodCall\FluentChainMethodCallToNormalMethodCallRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -38,7 +38,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(FuncCallToMethodCallRector::class)
->call('configure', [[
FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => inline_objects($configuration),
FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => inline_value_objects($configuration),
]]);
$services->set(StaticCallToFunctionRector::class)

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
use Rector\Generic\Rector\FuncCall\FuncCallToStaticCallRector;
use Rector\Generic\ValueObject\FuncNameToStaticCallName;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -57,6 +57,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(FuncCallToStaticCallRector::class)
->call('configure', [[
FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => inline_objects($configuration),
FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => inline_value_objects($configuration),
]]);
};

View File

@ -9,7 +9,7 @@ use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector;
use Rector\Renaming\ValueObject\StaticCallRename;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -55,7 +55,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(RenameStaticMethodRector::class)
->call('configure', [[
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_objects($configuration),
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_value_objects($configuration),
]]);
$services->set(RenameClassRector::class)

View File

@ -16,7 +16,7 @@ use Rector\NetteCodeQuality\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotate
use Rector\Renaming\Rector\ClassConstFetch\RenameClassConstantRector;
use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
@ -59,7 +59,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
);
$services->set(StaticCallToMethodCallRector::class)->call(
'configure',
[[StaticCallToMethodCallRector::STATIC_CALLS_TO_METHOD_CALLS => inline_objects($configuration)]]
[[StaticCallToMethodCallRector::STATIC_CALLS_TO_METHOD_CALLS => inline_value_objects($configuration)]]
);
// https://github.com/contributte/event-dispatcher-extra/tree/v0.4.3 and higher
$services->set(RenameClassConstantRector::class)->call(

View File

@ -13,7 +13,7 @@ use Rector\Nette\Rector\Identical\EndsWithFunctionToNetteUtilsStringsRector;
use Rector\Nette\Rector\Identical\StartsWithFunctionToNetteUtilsStringsRector;
use Rector\Nette\Rector\NotIdentical\StrposToStringsContainsRector;
use Rector\NetteUtilsCodeQuality\Rector\LNumber\ReplaceTimeNumberWithDateTimeConstantRector;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
# @see https://www.tomasvotruba.cz/blog/2018/07/30/hidden-gems-of-php-packages-nette-utils
@ -27,7 +27,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
];
$services->set(FuncCallToStaticCallRector::class)
->call('configure', [[
FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => inline_objects($configuration),
FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => inline_value_objects($configuration),
]]);
$services->set(StrposToStringsContainsRector::class);

View File

@ -20,7 +20,7 @@ use Rector\Renaming\Rector\MethodCall\RenameMethodRector;
use Rector\Renaming\Rector\Name\RenameClassRector;
use Rector\Renaming\Rector\StaticCall\RenameStaticMethodRector;
use Rector\Renaming\ValueObject\StaticCallRename;
use function Rector\SymfonyPhpConfig\inline_objects;
use function Rector\SymfonyPhpConfig\inline_value_objects;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
# see https://github.com/PHPOffice/PhpSpreadsheet/blob/master/docs/topics/migration-from-PHPExcel.md
@ -109,7 +109,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(RenameStaticMethodRector::class)
->call('configure', [[
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_objects($configuration),
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => inline_value_objects($configuration),
]]);
$services->set(RenameClassRector::class)

View File

@ -5106,7 +5106,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(FuncCallToMethodCallRector::class)
->call('configure', [[
FuncCallToMethodCallRector::FUNC_CALL_TO_CLASS_METHOD_CALL => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Generic\ValueObject\FuncNameToMethodCallName('view', 'Namespaced\SomeRenderer', 'render'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Generic\ValueObject\FuncNameToMethodCallName('view', 'Namespaced\SomeRenderer', 'render'))]
]]);
};
```
@ -5174,7 +5174,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(FuncCallToStaticCallRector::class)
->call('configure', [[
FuncCallToStaticCallRector::FUNC_CALLS_TO_STATIC_CALLS => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Generic\ValueObject\FuncNameToStaticCallName('view', 'SomeStaticClass', 'render'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Generic\ValueObject\FuncNameToStaticCallName('view', 'SomeStaticClass', 'render'))]
]]);
};
```
@ -6353,7 +6353,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(StaticCallToMethodCallRector::class)
->call('configure', [[
StaticCallToMethodCallRector::STATIC_CALLS_TO_METHOD_CALLS => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Injection\ValueObject\StaticCallToMethodCall('Nette\Utils\FileSystem', 'write', 'Symplify\SmartFileSystem\SmartFileSystem', 'dumpFile'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Injection\ValueObject\StaticCallToMethodCall('Nette\Utils\FileSystem', 'write', 'Symplify\SmartFileSystem\SmartFileSystem', 'dumpFile'))]
]]);
};
```
@ -12684,7 +12684,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(RenameStaticMethodRector::class)
->call('configure', [[
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Renaming\ValueObject\StaticCallRename('SomeClass', 'oldMethod', 'AnotherExampleClass', 'newStaticMethod'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Renaming\ValueObject\StaticCallRename('SomeClass', 'oldMethod', 'AnotherExampleClass', 'newStaticMethod'))]
]]);
};
```
@ -12707,7 +12707,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(RenameStaticMethodRector::class)
->call('configure', [[
RenameStaticMethodRector::OLD_TO_NEW_METHODS_BY_CLASSES => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Renaming\ValueObject\StaticCallRename('SomeClass', 'oldMethod', 'SomeClass', 'newStaticMethod'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Renaming\ValueObject\StaticCallRename('SomeClass', 'oldMethod', 'SomeClass', 'newStaticMethod'))]
]]);
};
```
@ -12741,7 +12741,7 @@ return function (ContainerConfigurator $containerConfigurator) : void {
$services->set(CompleteImportForPartialAnnotationRector::class)
->call('configure', [[
CompleteImportForPartialAnnotationRector::USE_IMPORTS_TO_RESTORE => [
\Rector\SymfonyPhpConfig\inline_object(new Rector\Restoration\ValueObject\UseWithAlias('Doctrine\ORM\Mapping', 'ORM'))]
\Rector\SymfonyPhpConfig\inline_value_object(new Rector\Restoration\ValueObject\UseWithAlias('Doctrine\ORM\Mapping', 'ORM'))]
]]);
};
```

View File

@ -1,7 +1,6 @@
<?php declare(strict_types=1);
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use PhpCsFixer\Fixer\ClassNotation\ClassAttributesSeparationFixer;
use PhpCsFixer\Fixer\Comment\NoTrailingWhitespaceInCommentFixer;
use PhpCsFixer\Fixer\Import\NoLeadingImportSlashFixer;
@ -9,21 +8,23 @@ use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
use PhpCsFixer\Fixer\Import\OrderedImportsFixer;
use PhpCsFixer\Fixer\Phpdoc\NoEmptyPhpdocFixer;
use PhpCsFixer\Fixer\Strict\DeclareStrictTypesFixer;
use Symplify\EasyCodingStandard\Configuration\Option;
use Symplify\EasyCodingStandard\ValueObject\Set\SetList;
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
$services->set(NoUnusedImportsFixer::class);
$services->set(OrderedImportsFixer::class);
$services->set(NoLeadingImportSlashFixer::class);
$services->set(NoTrailingWhitespaceInCommentFixer::class);
$services->set(NoEmptyPhpdocFixer::class);
$services->set(ClassAttributesSeparationFixer::class);
$services->set(DeclareStrictTypesFixer::class);
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::SETS, [
SetList::DOCTRINE_ANNOTATIONS,
]);
};

View File

@ -43,10 +43,11 @@ final class ArrayPartPhpDocTagPrinterTest extends TestCase
yield [['strict' => false], 'option', 'option={"strict":false}'];
// multiple items, separated by comma
yield [[
'less' => 'NO',
'more' => 'YES',
], 'what', 'what={"less":"NO", "more":"YES"}'];
yield [
[
'less' => 'NO',
'more' => 'YES',
], 'what', 'what={"less":"NO", "more":"YES"}', ];
// preslash
yield [['\John'], 'name', 'name={"\John"}'];

View File

@ -62,7 +62,7 @@ final class TemplateFinder
*/
private function addRuleAndTestCase(RectorRecipe $rectorRecipe, array $filePaths): array
{
if ($rectorRecipe->getConfigration() !== []) {
if ($rectorRecipe->getConfiguration() !== []) {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/src/Rector/__Category__/__Configured__Name__.php';
if ($rectorRecipe->getExtraFileContent()) {

View File

@ -66,7 +66,7 @@ final class TemplateVariablesFactory
}
/**
* @return string[]
* @return array<string, mixed>
*/
public function createFromRectorRecipe(RectorRecipe $rectorRecipe): array
{
@ -85,26 +85,26 @@ final class TemplateVariablesFactory
$rectorClass = $this->templateFactory->create(ConfigFilesystem::RECTOR_FQN_NAME_PATTERN, $data);
if ($rectorRecipe->getConfigration() !== []) {
if ($rectorRecipe->getConfiguration() !== []) {
$data['__TestRuleConfiguration__'] = $this->createRuleConfiguration(
$rectorClass,
$rectorRecipe->getConfigration()
$rectorRecipe->getConfiguration()
);
$data['__RuleConfiguration__'] = $this->createRuleConfiguration(
self::SELF,
$rectorRecipe->getConfigration()
$rectorRecipe->getConfiguration()
);
$data['__ConfigurationProperties__'] = $this->createConfigurationProperty(
$rectorRecipe->getConfigration()
$rectorRecipe->getConfiguration()
);
$data['__ConfigurationConstants__'] = $this->createConfigurationConstants(
$rectorRecipe->getConfigration()
$rectorRecipe->getConfiguration()
);
$data['__ConfigureClassMethod__'] = $this->createConfigureClassMethod(
$rectorRecipe->getConfigration()
$rectorRecipe->getConfiguration()
);
}

View File

@ -66,12 +66,13 @@ final class RectorRecipe
/**
* @var mixed[]
*/
private $configration = [];
private $configuration = [];
/**
* @var string|null
* Use default package name, if not overriden manually
* @var string
*/
private $package;
private $package = self::PACKAGE_UTILS;
/**
* @var string|null
@ -88,24 +89,15 @@ final class RectorRecipe
*/
private $extraFileContent;
/**
* @param class-string[] $nodeTypes
* @param mixed[] $configration
*/
public function __construct(
?string $package,
string $name,
array $nodeTypes,
string $description,
string $codeBefore,
string $codeAfter,
array $resources,
?string $set = null,
array $configration = [],
?string $extraFileName = null,
?string $extraFileContent = null,
?bool $isRectorRepository = null
string $codeAfter
) {
$this->isRectorRepository = file_exists(__DIR__ . '/../../../../vendor');
$this->setName($name);
$this->setNodeTypes($nodeTypes);
@ -113,28 +105,13 @@ final class RectorRecipe
$this->setCodeBefore($codeBefore);
$this->setCodeAfter($codeAfter);
$this->setResources($resources);
$this->set = $set;
$this->configration = $configration;
$this->extraFileName = $extraFileName;
$this->setExtraFileContent($extraFileContent);
$this->setIsRectorRepository($isRectorRepository);
// last on purpose, depends on isRectorRepository
$this->setPackage($package);
$this->resolveCategory();
$this->resolveCategory($nodeTypes);
}
public function getPackage(): string
{
$recipePackage = $this->package;
if (! $this->isRectorRepository || $recipePackage === null || $recipePackage === '') {
return self::PACKAGE_UTILS;
}
return $recipePackage;
return $this->package;
}
public function getName(): string
@ -167,6 +144,11 @@ final class RectorRecipe
return $this->resources;
}
public function setSet(string $set): void
{
$this->set = $set;
}
public function getSet(): ?string
{
return $this->set;
@ -175,9 +157,9 @@ final class RectorRecipe
/**
* @return array<string, mixed>
*/
public function getConfigration(): array
public function getConfiguration(): array
{
return $this->configration;
return $this->configuration;
}
public function getExtraFileName(): ?string
@ -219,6 +201,50 @@ final class RectorRecipe
return StaticRectorStrings::camelCaseToDashes($this->getPackage());
}
/**
* @api
*/
public function setPackage(string $package): void
{
$this->package = $package;
}
/**
* @api
*/
public function addExtraFile(string $extraFileName, string $extraFileContent): void
{
$this->extraFileName = $extraFileName;
$this->extraFileContent = $this->normalizeCode($extraFileContent);
}
/**
* @api
* @param mixed[] $configuration
*/
public function setConfiguration(array $configuration): void
{
$this->configuration = $configuration;
}
/**
* @api
* @param string[] $resources
*/
public function setResources(array $resources): void
{
$this->resources = array_filter($resources);
}
/**
* For testing purposes
* @api
*/
public function setIsRectorRepository(bool $isRectorRepository): void
{
$this->isRectorRepository = $isRectorRepository;
}
private function setName(string $name): void
{
if (! Strings::endsWith($name, 'Rector')) {
@ -267,45 +293,12 @@ final class RectorRecipe
$this->codeAfter = $this->normalizeCode($codeAfter);
}
private function setResources(array $resources): void
/**
* @param string[] $nodeTypes
*/
private function resolveCategory(array $nodeTypes): void
{
$this->resources = array_filter($resources);
}
private function setExtraFileContent(?string $extraFileContent): void
{
if ($extraFileContent === null) {
return;
}
$this->extraFileContent = $this->normalizeCode($extraFileContent);
}
private function setIsRectorRepository(?bool $isRectorRepository): void
{
$this->isRectorRepository = $isRectorRepository ?? file_exists(__DIR__ . '/../../../../vendor');
}
private function setPackage(?string $package): void
{
if ($package !== '' && $package !== null && $package !== self::PACKAGE_UTILS) {
$this->package = $package;
return;
}
// only can be empty or utils when outside Rector repository
if (! $this->isRectorRepository) {
$this->package = $package;
return;
}
$message = sprintf('Parameter "package" cannot be empty or "Utils", when in Rector repository');
throw new ConfigurationException($message);
}
private function resolveCategory(): void
{
$this->category = (string) Strings::after($this->nodeTypes[0], '\\', -1);
$this->category = (string) Strings::after($nodeTypes[0], '\\', -1);
}
private function detectIsPhpSnippet(string $codeBefore): void

View File

@ -6,17 +6,17 @@ namespace Rector\RectorGenerator\Tests\RectorGenerator\Source;
use PhpParser\Node\Expr\MethodCall;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Rector\Set\ValueObject\SetList;
final class StaticRectorRecipeFactory
{
public static function createRectorRecipe(bool $isRectorRepository): RectorRecipe
{
return new RectorRecipe(
'ModeratePackage',
$rectorRecipe = new RectorRecipe(
'WhateverRector',
[MethodCall::class],
'Change $service->arg(...) to $service->call(...)',
<<<'CODE_SAMPLE'
<<<'CODE_SAMPLE'
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -27,8 +27,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(SomeClass::class)
->arg('$key', 'value');
}
CODE_SAMPLE,
<<<'CODE_SAMPLE'
CODE_SAMPLE
, <<<'CODE_SAMPLE'
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -41,21 +41,23 @@ return static function (ContainerConfigurator $containerConfigurator): void {
'$key' => 'value'
]]);
}
CODE_SAMPLE,
// e.g. link to RFC or headline in upgrade guide, 1 or more in the list
[],
// e.g. symfony30, target set to add this Rule to; keep null if part of core
null,
// OPTIONAL: only when configured
[
'CLASS_TYPE_TO_METHOD_NAME' => [
'SomeClass' => 'configure'
]
],
null,
null,
$isRectorRepository
CODE_SAMPLE
);
$rectorRecipe->setConfiguration([
'CLASS_TYPE_TO_METHOD_NAME' => [
'SomeClass' => 'configure'
]
]);
$rectorRecipe->setIsRectorRepository($isRectorRepository);
if ($isRectorRepository) {
$rectorRecipe->setPackage('ModeratePackage');
}
$rectorRecipe->setSet(SetList::DEAD_CODE);
return $rectorRecipe;
}
}

View File

@ -56,6 +56,9 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/packages/doctrine-annotation-generated/src/*',
// tempalte files
__DIR__ . '/packages/rector-generator/templates/*',
// public api
__DIR__ . '/packages/rector-generator/src/ValueObject/RectorRecipe.php',
__DIR__ . '/rules/symfony-php-config/functions/functions.php',
]);
$parameters->set(Option::EXCLUDE_RECTORS, [

View File

@ -2,27 +2,29 @@
declare(strict_types=1);
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Expr\MethodCall;
use Rector\RectorGenerator\Provider\RectorRecipeProvider;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Rector\SymfonyPhpConfig\inline_object;
use function Rector\SymfonyPhpConfig\inline_single_object;
// run "bin/rector generate" to a new Rector basic schema + tests from this config
return static function (ContainerConfigurator $containerConfigurator): void {
$services = $containerConfigurator->services();
// [REQUIRED]
$rectorRecipe = new RectorRecipe(
// category
'Symfony',
// name
'RenameClassMethod',
// node types - this will get in "getNodeTypes()" method
[ClassMethod::class],
// description
'Change some class method to different name',
// name, basically short class name; use PascalCase
'RenameMethodCallRector',
// code before
// particular node types to change
// the best practise is to have just 1 type here if possible, and make separated rule for other node types
[MethodCall::class],
// describe what the rule does
'"something()" will be renamed to "somethingElse()"',
// code before change
// this is used for documentation and first test fixture
<<<'CODE_SAMPLE'
<?php
@ -30,40 +32,51 @@ class SomeClass
{
public function run()
{
$this->something();
}
}
CODE_SAMPLE,
// code after
<<<'CODE_SAMPLE'
CODE_SAMPLE
// code after change
, <<<'CODE_SAMPLE'
<?php
class SomeClass
{
public function go()
public function run()
{
$this->somethingElse();
}
}
CODE_SAMPLE,
// informational resources on "why" the change, e.g. RFC, upgrade guide on Github
[
'https://...'
],
// OPTIONAL ↓
// e.g. symfony30, target set to add this Rule to; keep null if part of core
// set, use `SetList::SOME_CONSTANT` for this
null,
// configuration in array
[],
// extra file name
null,
// extra file content
null
CODE_SAMPLE
);
// [OPTIONAL - UNCOMMENT TO USE]
// links to useful websites, that explain why the change is needed
// $rectorRecipe->setResources([
// 'https://github.com/symfony/symfony/blob/704c648ba53be38ef2b0105c97c6497744fef8d8/UPGRADE-6.0.md'
// ]);
// do you need to check for extra generated file?
// $rectorRecipe->addExtraFile('SomeExtraFile.php', '<?php ... SomeExtraFileContent');
// is the rule configurable? add default configuration here
// $rectorRecipe->addConfiguration(['SOME_CONSTANT_KEY' => ['before' => 'after']]);
// [RECTOR CORE CONTRIBUTION]
// [RECTOR CORE CONTRIBUTION - REQUIRED]
// package name, basically part namespace in `rule/<package>/src`, use PascalCase
// $rectorRecipe->setPackage('Generic');
// [RECTOR CORE CONTRIBUTION - OPTIONAL]
// set the rule belongs to; is optional, because e.g. generic rules don't need a specific set to belong to
// $rectorRecipe->setSet(\Rector\Set\ValueObject\SetList::NAMING);
$services = $containerConfigurator->services();
$services->set(RectorRecipeProvider::class)
->arg('$rectorRecipe', inline_object($rectorRecipe));
->arg('$rectorRecipe', inline_single_object($rectorRecipe, $services));
};

View File

@ -1,396 +0,0 @@
includes:
- utils/phpstan-extensions/config/phpstan-extensions.neon
- vendor/symplify/phpstan-extensions/config/config.neon
- vendor/thecodingmachine/phpstan-strict-rules/phpstan-strict-rules.neon
- vendor/slam/phpstan-extensions/conf/slam-rules.neon
# see https://github.com/symplify/coding-standard
- vendor/symplify/coding-standard/config/symplify-rules.neon
rules:
# should be fixed in next part of symplify CS release
- Symplify\CodingStandard\Rules\NoClassWithStaticMethodWithoutStaticNameRule
- Symplify\CodingStandard\Rules\SeeAnnotationToTestRule
services:
# this rule prevents bug in phar like these: https://github.com/rectorphp/rector/pull/3692/files
-
class: Rector\PHPStanExtensions\Rule\RequireStringArgumentInMethodCallRule
tags: [phpstan.rules.rule]
arguments:
constantArgByMethodByType:
Rector\Core\Rector\AbstractRector:
isObjectType: [1]
parameters:
level: max
# see https://github.com/symplify/coding-standard
symplify:
max_cognitive_complexity: 9 # default: 8
max_class_cognitive_complexity: 50
parent_classes:
- Rector
required_see_types:
- PHPStan\Rules\Rule
- Rector\Core\Rector\AbstractRector
- Rector\FileSystemRector\Rector\AbstractFileSystemRector
old_to_preffered_classes:
# prevent PHPStorm autocomplete mess
'Symfony\Component\DependencyInjection\Variable': 'PhpParser\Node\Expr\Variable'
'phpDocumentor\Reflection\Types\Expression': 'PhpParser\Node\Stmt\Expression'
'phpDocumentor\Reflection\DocBlock\Tags\Param': 'PhpParser\Node\Param'
'phpDocumentor\Reflection\DocBlock\Tags\Return_': 'PhpParser\Node\Stmt\Return_'
'Closure': 'PhpParser\Node\Expr\Closure'
'PHPUnit\TextUI\Configuration\Variable': 'PhpParser\Node\Expr\Variable'
'PhpCsFixer\FixerDefinition\CodeSample': 'Rector\Core\RectorDefinition\CodeSample'
'SebastianBergmann\Type\MixedType': 'PHPStan\Type\MixedType'
'Hoa\Protocol\Node\Node': 'PhpParser\Node'
# to allow installing with various phsptan versions without reporting old errors here
reportUnmatchedIgnoredErrors: false
checkGenericClassInNonGenericObjectType: false
scanDirectories:
- stubs
bootstrapFiles:
- vendor/symfony/dependency-injection/Loader/Configurator/ContainerConfigurator.php
paths:
- bin
- src
- rules
- packages
- tests
- compiler/src
- utils
# this cannot be put it, because it wipes PHPStan cache on each run
#- config
excludes_analyse:
# iterable types
- '#with no value type specified in iterable type array#'
- '#type specified in iterable type (array|iterable)#'
# phsptan bug
- utils/phpstan-extensions/src/Rule/PreventParentMethodVisibilityOverrideRule.php
- utils/phpstan-extensions/src/Rule/KeepRectorNamespaceForRectorRule.php
- packages/rector-generator/templates/*
# generated files
- 'packages/doctrine-annotation-generated/src/ConstantPreservingDocParser.php'
- 'packages/doctrine-annotation-generated/src/ConstantPreservingAnnotationReader.php'
- "*/Expected/*"
# complex printer
- '*tests/Rector/MethodCall/RenameMethodRector/**/SomeClass.php'
# tests files
- '*tests/*/Fixture/*'
- '*tests/*/Source/*'
- '*tests/Source/*'
# part of composer
- '*/tests/Rector/Psr4/MultipleClassFileToPsr4ClassesRector/Expected/Just*ExceptionWithoutNamespace.php'
ignoreErrors:
# @todo remove
# iterable types
- '#with no value type specified in iterable type array#'
- '#type specified in iterable type (array|iterable)#'
# false positive
- '#PHPDoc tag \@param for parameter \$node with type float is incompatible with native type PhpParser\\Node#'
# misuse of interface and class
- '#Parameter \#1 (.*?) expects Symfony\\Component\\DependencyInjection\\ContainerBuilder, Symfony\\Component\\DependencyInjection\\ContainerInterface given#'
- '#Strict comparison using === between string and null will always evaluate to false#'
# false positive - type is set by annotation above
- '#Array \(array<PhpParser\\Node\\Stmt>\) does not accept PhpParser\\Node#'
# irrelevant
- '#Call to function in_array\(\) with arguments string, (.*?) and true will always evaluate to false#'
# known values
- '#Access to an undefined property PhpParser\\Node\\Expr::\$right#'
- '#Access to an undefined property PhpParser\\Node\\Expr\\MethodCall\|PhpParser\\Node\\Stmt\\ClassMethod::\$params#'
- '#Cannot call method getName\(\) on PHPStan\\Reflection\\ClassReflection\|null#'
# false positive, has annotation type above
- '#Method Rector\\CodeQuality\\Rector\\Foreach_\\SimplifyForeachToCoalescingRector\:\:matchReturnOrAssignNode\(\) should return PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Stmt\\Return_\|null but returns PhpParser\\Node\|null#'
- '#Access to an undefined property PhpParser\\Node::\$(\w+)#'
# intentionally incorrect - part of the test
- '#Parameter \#2 \$codeSamples of class Rector\\Core\\RectorDefinition\\RectorDefinition constructor expects array<Rector\\Core\\Contract\\RectorDefinition\\CodeSampleInterface>, array<int, stdClass> given#'
# known values
- '#Cannot access property \$value on PhpParser\\Node\\Expr\\ArrayItem\|null#'
# known values
- '#Strict comparison using === between PhpParser\\Node\\Expr and null will always evaluate to false#'
- '#Access to an undefined property PhpParser\\Node\\Stmt\:\:\$expr#'
- '#Cannot access property \$stmts on PhpParser\\Node\\Stmt\\Else_\|null#'
# node finder
- '#Method Rector\\(.*?) should return array<PhpParser\\Node\\(.*?)> but returns array<PhpParser\\Node\>#'
# part of test
- '#(.*?)(AttributeAwareNodeInterface|AttributeAware(.*?)TagValueNode)(.*?)#'
- '#Parameter \#1 \$children of class PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode constructor expects array<PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocChildNode\>, array<int, PHPStan\\PhpDocParser\\Ast\\Node\> given#'
- '#Method Rector\\PHPUnit\\Rector\\MethodCall\\ReplaceAssertArraySubsetRector\:\:matchArray\(\) should return PhpParser\\Node\\Expr\\Array_\|null but returns PhpParser\\Node\\Expr#'
- '#(.*?)PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Expr\\Variable given#'
# false positive 0.11.5
- '#Unreachable statement \- code above always terminates#'
- '#Negated boolean expression is always true#'
- '#Strict comparison using \=\=\= between PhpParser\\Node and null will always evaluate to false#'
# known types
- '#Access to an undefined property PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Expr\\Variable\:\:\$name#'
- '#Strict comparison using \=\=\= between PhpParser\\Node\\Expr\\ArrayItem and null will always evaluate to false#'
- '#Parameter \#2 \.\.\.\$args of function array_merge expects array, array<int, string\>\|false given#'
- '#Access to an undefined property PhpParser\\Node\\Expr\:\:\$args#'
- '#Parameter \#2 \$name of method Rector\\Core\\Rector\\AbstractRector\:\:isName\(\) expects string, string\|null given#'
# cascade irrelevant
- '#Parameter (.*?) expects array<PhpParser\\Node\\Stmt\>, array<PhpParser\\Node\> given#'
# known value
- '#Cannot cast array<string\>\|bool\|string\|null to string#'
- '#Method Rector\\Legacy\\Rector\\Class_\\ChangeSingletonToServiceRector\:\:matchStaticPropertyFetchAndGetSingletonMethodName\(\) should return array<string\>\|null but returns array<int, string\|null\>#'
- '#Parameter \#2 \$currentNode of method Rector\\CodingStyle\\Rector\\Assign\\ManualJsonStringToJsonEncodeArrayRector\:\:matchNextExpressionAssignConcatToSameVariable\(\) expects PhpParser\\Node\\Expr\\Assign\|PhpParser\\Node\\Expr\\AssignOp\\Concat, PhpParser\\Node given#'
# array is callable
- '#If condition is always true#'
- '#Ternary operator condition is always true#'
- '#Access to an undefined property PhpParser\\Node\\FunctionLike\|PhpParser\\Node\\Stmt\\ClassLike\:\:\$stmts#'
- '#Property Rector\\TypeDeclaration\\TypeInferer\\(.*?)\:\:\$(.*?)TypeInferers \(array<Rector\\TypeDeclaration\\Contract\\TypeInferer\\(.*?)TypeInfererInterface\>\) does not accept array<Rector\\TypeDeclaration\\Contract\\TypeInferer\\PriorityAwareTypeInfererInterface\>#'
# sense-less errors
# 3rd party
-
message: '#Use default null value and nullable compare instead of isset on object#'
path: 'rules/symfony/src/ServiceMapProvider.php'
# PHP 7.4 1_000 support
- '#Property PhpParser\\Node\\Scalar\\DNumber\:\:\$value \(float\) does not accept string#'
- '#Call to function is_string\(\) with float will always evaluate to false#'
- '#Method Rector\\Doctrine\\Rector\\MethodCall\\ChangeSetIdToUuidValueRector\:\:getSetUuidMethodCallOnSameVariable\(\) should return PhpParser\\Node\\Expr\\MethodCall\|null but returns PhpParser\\Node\|null#'
# known value
- '#Method Rector\\StrictCodeQuality\\Rector\\Stmt\\VarInlineAnnotationToAssertRector\:\:findVariableByName\(\) should return PhpParser\\Node\\Expr\\Variable\|null but returns PhpParser\\Node\|null#'
- '#Method Rector\\NodeTypeResolver\\PHPStan\\Type\\TypeFactory\:\:createUnionOrSingleType\(\) should return PHPStan\\Type\\MixedType\|PHPStan\\Type\\UnionType but returns PHPStan\\Type\\Type#'
# test
- '#Class Rector\\DynamicTypeAnalysis\\Tests\\Rector\\ClassMethod\\AddArgumentTypeWithProbeDataRector\\Fixture\\SomeClass not found#'
-
message: '#Class Rector\\Generic\\Tests\\Rector\\StaticCall\\SwapClassMethodArgumentsRector\\Fixture\\SomeClass not found#'
path: rules/generic/tests/Rector/StaticCall/SwapClassMethodArgumentsRector/SwapClassMethodArgumentsRectorTest.php
# internal rule
- '#Class "Rector\\Utils\\(.*?)" is missing @see annotation with test case class reference#'
# mixed
- '#Offset int\|string\|null does not exist on array<PhpParser\\Node\\Stmt>\|null#'
- '#class-string<T of object>\|T of object#'
# known values
- '#Offset 0 does not exist on array<PhpParser\\Node\\Stmt>\|null#'
- '#Parameter \#1 \$left of class PhpParser\\Node\\Expr\\BinaryOp\\Spaceship constructor expects PhpParser\\Node\\Expr, PhpParser\\Node\\Expr\|null given#'
- '#Parameter \#2 \$right of class PhpParser\\Node\\Expr\\BinaryOp\\Spaceship constructor expects PhpParser\\Node\\Expr, PhpParser\\Node\\Expr\|null given#'
- '#Parameter \#3 \$nodeCallback of method PHPStan\\Analyser\\NodeScopeResolver::processNodes\(\) expects Closure\(PhpParser\\Node, PHPStan\\Analyser\\Scope\): void, Closure\(PhpParser\\Node, PHPStan\\Analyser\\MutatingScope\): void given#'
# false positive
- '#Comparison operation "<" between 0 and 2 is always true#'
- '#Method Rector\\Symfony\\Rector\\MethodCall\\AbstractToConstructorInjectionRector\:\:getServiceTypeFromMethodCallArgument\(\) should return PHPStan\\Type\\Type but returns PHPStan\\Type\\Type\|null#'
- '#Parameter \#1 \$expected of method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) expects class\-string<object\>, string given#'
- '#Unable to resolve the template type ExpectedType in call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\)#'
# fix Symplify 7.2 later
- '#Method (.*?) returns bool type, so the name should start with is/has/was#'
# known value
- '#Cannot cast \(array<string\>\)\|string\|true to string#'
- '#In method "Rector\\BetterPhpDocParser\\AnnotationReader\\NodeAnnotationReader\:\:createPropertyReflectionFromPropertyNode", caught "Throwable" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception\. More info\: http\://bit\.ly/failloud#'
# doc is not enough
- '#Result of \|\| is always true#'
# known value
- '#Parameter \#2 \$name of class PhpParser\\Node\\Expr\\MethodCall constructor expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Identifier\|string, string\|null given#'
- '#Parameter \#1 \$eventListenerTag of method Rector\\SymfonyCodeQuality\\Rector\\Class_\\EventListenerToEventSubscriberRector\:\:createEventItem\(\) expects Rector\\Symfony\\ValueObject\\Tag\\EventListenerTag, Rector\\Symfony\\Contract\\Tag\\TagInterface given#'
- '#Method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfoFactory\:\:parseTokensToPhpDocNode\(\) should return Rector\\AttributeAwarePhpDoc\\Ast\\PhpDoc\\AttributeAwarePhpDocNode but returns PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocNode#'
- '#Property PhpParser\\Node\\Stmt\\Expression\:\:\$expr \(PhpParser\\Node\\Expr\) does not accept PhpParser\\Node\\Expr\|null#'
- '#Call to an undefined method PHPStan\\Type\\Type\:\:getClassName\(\)#'
- '#Parameter \#1 \$typeNode of method Rector\\StaticTypeMapper\\StaticTypeMapper\:\:mapPHPStanPhpDocTypeNodeToPHPStanType\(\) expects PHPStan\\PhpDocParser\\Ast\\Type\\TypeNode, PHPStan\\PhpDocParser\\Ast\\Node given#'
- '#Parameter \#1 \$str of function preg_quote expects string, int\|string given#'
- '#Parameter \#1 \$sprintfFuncCall of method Rector\\Core\\PhpParser\\NodeTransformer\:\:transformSprintfToArray\(\) expects PhpParser\\Node\\Expr\\FuncCall, PhpParser\\Node given#'
- '#Parameter \#1 \$nodes of method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:find\(\) expects array<PhpParser\\Node\>\|PhpParser\\Node, array<PhpParser\\Node\\Stmt\>\|null given#'
- '#Method Rector\\SOLID\\Reflection\\ParentConstantReflectionResolver\:\:(.*?)\(\) should return ReflectionClassConstant\|null but returns ReflectionClassConstant\|false#'
- '#Parameter \#1 \$firstStmt of method Rector\\Generic\\Rector\\ClassMethod\\NormalToFluentRector\:\:isBothMethodCallMatch\(\) expects PhpParser\\Node\\Stmt\\Expression, PhpParser\\Node\\Stmt given#'
- '#Method Rector\\Core\\Rector\\AbstractRector\:\:wrapToArg\(\) should return array<PhpParser\\Node\\Arg\> but returns array<PhpParser\\Node\\Arg\|PhpParser\\Node\\Expr\>#'
- '#Method Rector\\FileSystemRector\\Rector\\AbstractFileSystemRector\:\:wrapToArg\(\) should return array<PhpParser\\Node\\Arg\> but returns array<PhpParser\\Node\\Arg\|PhpParser\\Node\\Expr\>#'
- '#Cannot call method (.*?)\(\) on Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\|null#'
- '#Right side of && is always true#'
- '#Parameter \#(.*?) (.*?) of class PhpParser\\Node\\Expr\\BinaryOp\\(.*?) constructor expects PhpParser\\Node\\Expr, PhpParser\\Node given#'
- '#Access to an undefined property PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode\:\:\$description#'
- '#Method Rector\\Php80\\Rector\\NotIdentical\\StrContainsRector\:\:matchNotIdenticalToFalse\(\) should return PhpParser\\Node\\Expr\\FuncCall\|null but returns PhpParser\\Node\\Expr#'
- '#Parameter \#2 \$name of method Rector\\Core\\Rector\\AbstractRector\:\:isVariableName\(\) expects string, string\|null given#'
# node finder
- '#Method Rector\\Core\\PhpParser\\Node\\Manipulator\\MethodCallManipulator\:\:findAssignToVariableName\(\) should return PhpParser\\Node\\Expr\\Assign\|null but returns PhpParser\\Node\|null#'
# broken
- '#Cannot call method getParentNode\(\) on Rector\\DeadCode\\ValueObject\\VariableNodeUse\|null#'
- '#Method Rector\\DeadCode\\NodeFinder\\PreviousVariableAssignNodeFinder\:\:find\(\) should return PhpParser\\Node\\Expr\\Assign\|null but returns PhpParser\\Node\|null#'
- '#Parameter \#2 \$name of method Rector\\NodeNameResolver\\NodeNameResolver\:\:isName\(\) expects string, string\|null given#'
- '#Method Rector\\PHPOffice\\Rector\\MethodCall\\IncreaseColumnIndexRector\:\:findVariableAssignName\(\) should return PhpParser\\Node\\Expr\\Assign\|null but returns PhpParser\\Node\|null#'
- '#Parameter \#1 \$keyName of method Rector\\AttributeAwarePhpDoc\\Ast\\Type\\AttributeAwareArrayShapeItemNode\:\:createKeyWithSpacePattern\(\) expects PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode\|PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode\|null, PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprIntegerNode\|PHPStan\\PhpDocParser\\Ast\\ConstExpr\\ConstExprStringNode\|PHPStan\\PhpDocParser\\Ast\\Type\\IdentifierTypeNode\|null given#'
- '#Method Rector\\Caching\\ChangedFilesDetector\:\:hashFile\(\) should return string but returns string\|false#'
- '#If condition is always false#'
- '#Parameter \#1 \$funcCall of method Rector\\Php80\\MatchAndRefactor\\StrStartsWithMatchAndRefactor\\AbstractMatchAndRefactor\:\:createStrStartsWithValueObjectFromFuncCall\(\) expects PhpParser\\Node\\Expr\\FuncCall, PhpParser\\Node\\Expr given#'
# mostly strings in tests
- '#Class (.*?) should be written with \:\:class notation, string found#'
- '#Parameter \#2 \$key of method Rector\\BetterPhpDocParser\\PhpDocNode\\AbstractTagValueNode\:\:printArrayItem\(\) expects string\|null, int\|string given#'
- '#Method Rector\\Naming\\Naming\\PropertyNaming\:\:resolveShortClassName\(\) should return string but returns string\|null#'
-
message: "#^Class cognitive complexity for \"PhpDocInfo\" is 53, keep it under 50$#"
count: 1
path: packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfo.php
-
message: "#in iterable type Iterator#"
paths:
- *Test.php
- *TestCase.php
-
message: "#^Cognitive complexity for \"Rector\\\\BetterPhpDocParser\\\\Printer\\\\WhitespaceDetector\\:\\:detectOldWhitespaces\\(\\)\" is 18, keep it under 9$#"
count: 1
path: packages/better-php-doc-parser/src/Printer/WhitespaceDetector.php
-
message: "#^Parameter \\#1 \\$input of function array_splice expects array, array\\<PhpParser\\\\Node\\\\Stmt\\>\\|null given\\.$#"
count: 1
path: rules/coding-style/src/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php
-
message: "#^Cognitive complexity for \"Rector\\\\PhpSpecToPHPUnit\\\\Rector\\\\MethodCall\\\\PhpSpecPromisesToPHPUnitAssertRector\\:\\:refactor\\(\\)\" is 13, keep it under 9$#"
count: 1
path: rules/php-spec-to-phpunit/src/Rector/MethodCall/PhpSpecPromisesToPHPUnitAssertRector.php
-
message: "#^Class cognitive complexity for \"EregToPcreTransformer\" is (.*?), keep it under 50$#"
path: rules/php70/src/EregToPcreTransformer.php
-
message: "#Use explicit property fetch names over dynamic#"
path: packages/doctrine-annotation-generated/src/PhpDocNode/ConstantReferenceIdentifierRestorer.php
- "#^Cognitive complexity for \"Rector\\\\Php70\\\\EregToPcreTransformer\\:\\:(.*?)\" is (.*?), keep it under 9$#"
- '#Use explicit return value over magic &reference#'
- '#In method "Rector\\Utils\\ProjectValidator\\Process\\ParallelTaskRunner\:\:(.*?)", caught "Throwable" must be rethrown\. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception#'
# weird
- '#Method (.*?) specified in iterable type Symfony\\Component\\Process\\Process#'
- '#Cannot cast PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Identifier to string#'
- '#Class cognitive complexity for "DumpNodesCommand" is \d+, keep it under 50#'
- '#Cognitive complexity for "Rector\\Utils\\DocumentationGenerator\\Command\\DumpNodesCommand\:\:execute\(\)" is \d+, keep it under 9#'
- '#Parameter \#1 \$node of method Rector\\PostRector\\Collector\\NodesToAddCollector\:\:wrapToExpression\(\) expects PhpParser\\Node\\Expr\|PhpParser\\Node\\Stmt, PhpParser\\Node given#'
- '#Access to an undefined property PhpParser\\Node\\Expr\:\:\$class#'
- '#Method Rector\\BetterPhpDocParser\\Tests\\PhpDocParser\\AbstractPhpDocInfoTest\:\:parseFileAndGetFirstNodeOfType\(\) should return PhpParser\\Node but returns PhpParser\\Node\|null#'
- '#Property PhpParser\\Node\\Stmt\\Namespace_\:\:\$stmts \(array<PhpParser\\Node\\Stmt\>\) does not accept array<PhpParser\\Node\>#'
- '#Cognitive complexity for "Rector\\TypeDeclaration\\PHPStan\\Type\\ObjectTypeSpecifier\:\:matchShortenedObjectType\(\)" is 10, keep it under 9#'
- '#Parameter \#1 \$type of method PhpParser\\Builder\\FunctionLike\:\:setReturnType\(\) expects PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|string, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#'
- '#Cognitive complexity for "Rector\\Core\\PhpParser\\Node\\Value\\ValueResolver\:\:getValue\(\)" is \d+, keep it under 9#'
- '#Cognitive complexity for "Rector\\NetteKdyby\\ContributeEventClassResolver\:\:resolveGetterMethodByEventClassAndParam\(\)" is \d+, keep it under 9#'
- '#Parameter \#1 \$type of class PhpParser\\Node\\NullableType constructor expects PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|string, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#'
- '#Parameter \#1 \$object of function get_class expects object, PhpParser\\Node\|null given#'
- '#Class "Rector\\FileSystemRector\\Rector\\Removing\\RemoveProjectFileRector" is missing @see annotation with test case class reference#'
- '#Parameter \#1 \$type of method PhpParser\\Builder\\Param\:\:setType\(\) expects PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType\|string, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\|PhpParser\\Node\\UnionType given#'
- '#Parameter \#1 \$node of method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:findFirstAncestorInstanceOf\(\) expects PhpParser\\Node, PhpParser\\Node\\Expr\\Variable\|null given#'
- '#Parameter \#1 \$objectType of method Rector\\Naming\\Naming\\PropertyNaming\:\:fqnToVariableName\(\) expects PHPStan\\Type\\ObjectType\|string, PHPStan\\Type\\Type given#'
- '#Method Rector\\Core\\PhpParser\\Node\\NodeFactory\:\:createConcat\(\) should return PhpParser\\Node\\Expr\\BinaryOp\\Concat\|null but returns PhpParser\\Node\\Expr#'
- '#Method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:findFirstNonAnonymousClass\(\) should return PhpParser\\Node\\Stmt\\Class_\|null but returns PhpParser\\Node\|null#'
# known value
- '#Property PhpParser\\Node\\Stmt\\Foreach_\:\:\$valueVar \(PhpParser\\Node\\Expr\) does not accept PhpParser\\Node\\Expr\|null#'
- '#Access to an undefined property PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode\:\:\$type#'
# local type
-
message: '#Method call "isObjectType\(\)" argument on position 1 cannot use "\:\:class" reference#'
path: 'packages/dynamic-type-analysis/src/Rector/StaticCall/RemoveArgumentTypeProbeRector.php'
# only local use
-
message: '#Class "Rector\\RectorGenerator\\Rector\\Closure\\AddNewServiceToSymfonyPhpConfigRector" is missing @see annotation with test case class reference#'
path: 'packages/rector-generator/src/Rector/Closure/AddNewServiceToSymfonyPhpConfigRector.php'
- '#Call to an undefined method PhpParser\\Node\\Expr\\Error\|PhpParser\\Node\\Identifier\:\:toString\(\)#'
- '#Class Rector\\Renaming\\Tests\\Rector\\MethodCall\\RenameMethodRector\\Fixture\\SkipSelfMethodRename not found#'
# fixed in symplfiy dev
-
message: '#Separate function "Symfony\\Component\\DependencyInjection\\Loader\\Configurator\\ref\(\)" in method call to standalone row to improve readability#'
path: 'packages/rector-generator/config/config.php'
- '#Method Rector\\Core\\PhpParser\\Node\\BetterNodeFinder\:\:findPreviousAssignToExpr\(\) should return PhpParser\\Node\\Expr\\Assign\|null but returns PhpParser\\Node\|null#'
- '#Parameter \#1 \$shortControlString of method Rector\\NetteCodeQuality\\Rector\\Assign\\MakeGetComponentAssignAnnotatedRector\:\:resolveTypeFromShortControlNameAndVariable\(\) expects PhpParser\\Node\\Scalar\\String_, PhpParser\\Node\\Expr\|null given#'
- '#Parameter \#1 \$variable of class Rector\\Php70\\ValueObject\\VariableAssignPair constructor expects PhpParser\\Node\\Expr\\ArrayDimFetch\|PhpParser\\Node\\Expr\\PropertyFetch\|PhpParser\\Node\\Expr\\StaticPropertyFetch\|PhpParser\\Node\\Expr\\Variable, PhpParser\\Node\\Expr given#'
# is nested expr
- '#Access to an undefined property PhpParser\\Node\\Expr\:\:\$expr#'
- '#Cognitive complexity for "Rector\\DeadCode\\NodeManipulator\\LivingCodeManipulator\:\:keepLivingCodeFromExpr\(\)" is \d+, keep it under 9#'
- '#Parameter \#1 \$files of method Symplify\\SmartFileSystem\\Finder\\FinderSanitizer\:\:sanitize\(\) expects \(iterable<SplFileInfo\|string\>&Nette\\Utils\\Finder\)\|Symfony\\Component\\Finder\\Finder, array<string\> given#'
- '#Static property Rector\\Core\\Testing\\PHPUnit\\AbstractGenericRectorTestCase\:\:\$allRectorContainer \(Rector\\Naming\\Tests\\Rector\\Class_\\RenamePropertyToMatchTypeRector\\Source\\ContainerInterface\|Symfony\\Component\\DependencyInjection\\Container\|null\) does not accept Psr\\Container\\ContainerInterface#'
# stubs
- '#Static property Symplify\\PackageBuilder\\Tests\\AbstractKernelTestCase\:\:\$container \(Psr\\Container\\ContainerInterface\) does not accept Rector\\Naming\\Tests\\Rector\\Class_\\RenamePropertyToMatchTypeRector\\Source\\ContainerInterface\|Symfony\\Component\\DependencyInjection\\Container#'
# wtf
-
message: '#Else branch is unreachable because ternary operator condition is always true#'
path: 'rules/psr4/src/Composer/PSR4NamespaceMatcher.php'
# false positive
- '#Parameter \#1 \$arrayItem of method Rector\\NetteKdyby\\NodeResolver\\ListeningMethodsCollector\:\:resolveCustomClassMethodAndEventClass\(\) expects PhpParser\\Node\\Expr\\ArrayItem, PhpParser\\Node given#'
- '#Parameter \#1 \$type of method Rector\\NodeCollector\\NodeCollector\\ParsedNodeCollector<TNodeType of PhpParser\\Node\>\:\:getNodesByType\(\) expects class\-string<TNodeType of PhpParser\\Node\>, string given#'
- '#Class with base "(.*?)" name is already used in "_HumbugBox(.*?)"#'
-
message: '#Class "Rector\\RectorGenerator\\ValueObject\\RectorRecipe" has invalid namespace category "ValueObject"\. Pick one of\: ""#'
path: packages/rector-generator/src/ValueObject/RectorRecipe.php

View File

@ -3,15 +3,12 @@
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import(__DIR__ . '/rector-recipe.php', null, 'not_found');
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
//$parameters->set(Option::SETS, [SetList::NAMING]);
$parameters->set(Option::PATHS, [
__DIR__ . '/src',
__DIR__ . '/tests',

View File

@ -5,11 +5,34 @@ declare(strict_types=1);
namespace Rector\SymfonyPhpConfig;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\SymfonyPhpConfig\Reflection\ArgumentAndParameterFactory;
use ReflectionClass;
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service;
use Symfony\Component\DependencyInjection\Loader\Configurator\InlineServiceConfigurator;
use Symfony\Component\DependencyInjection\Loader\Configurator\ReferenceConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\service;
use Symfony\Component\DependencyInjection\Loader\Configurator\ServicesConfigurator;
function inline_object(object $object): InlineServiceConfigurator
function inline_single_object(object $object, ServicesConfigurator $servicesConfigurator): ReferenceConfigurator
{
$reflectionClass = new ReflectionClass($object);
$className = $reflectionClass->getName();
$propertyValues = resolve_property_values($reflectionClass, $object);
$argumentValues = resolve_argument_values($reflectionClass, $object);
// create fake factory with private accessor, as properties are different
// @see https://symfony.com/doc/current/service_container/factories.html#passing-arguments-to-the-factory-method
$servicesConfigurator->set(ArgumentAndParameterFactory::class);
$servicesConfigurator->set($className)
->factory([service(ArgumentAndParameterFactory::class), 'create'])
->args([$className, $argumentValues, $propertyValues]);
return service($className);
}
function inline_value_object(object $object): InlineServiceConfigurator
{
$reflectionClass = new ReflectionClass($object);
@ -19,15 +42,16 @@ function inline_object(object $object): InlineServiceConfigurator
return inline_service($className)->args($argumentValues);
}
/**
* @param object[] $objects
* @return InlineServiceConfigurator[]
*/
function inline_objects(array $objects): array
function inline_value_objects(array $objects): array
{
$inlineServices = [];
foreach ($objects as $object) {
$inlineServices[] = inline_object($object);
$inlineServices[] = inline_value_object($object);
}
return $inlineServices;
@ -59,3 +83,20 @@ function resolve_argument_values(ReflectionClass $reflectionClass, object $objec
return $argumentValues;
}
/**
* @return array<string, mixed>
*/
function resolve_property_values(ReflectionClass $reflectionClass, object $object): array
{
$propertyValues = [];
foreach ($reflectionClass->getProperties() as $reflectionProperty) {
$parameterName = $reflectionProperty->getName();
$reflectionProperty->setAccessible(true);
$propertyValues[$parameterName] = $reflectionProperty->getValue($object);
}
return $propertyValues;
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
namespace Rector\SymfonyPhpConfig\Reflection;
use Symplify\PackageBuilder\Reflection\PrivatesAccessor;
final class ArgumentAndParameterFactory
{
/**
* @var PrivatesAccessor
*/
private $privatesAccessor;
public function __construct()
{
$this->privatesAccessor = new PrivatesAccessor();
}
/**
* @param array<string, mixed> $arguments
* @param array<string, mixed> $properties
*/
public function create(string $className, array $arguments, array $properties): object
{
$object = new $className(...$arguments);
foreach ($properties as $name => $value) {
$this->privatesAccessor->setPrivateProperty($object, $name, $value);
}
return $object;
}
}

View File

@ -81,6 +81,12 @@ final class RectorConfigsResolver
$setFileInfos = $this->resolveSetFileInfosFromConfigFileInfos($configFileInfos);
// autoload rector recipe file if present
$rectorRecipeFilePath = getcwd() . '/rector-recipe.php';
if (file_exists($rectorRecipeFilePath)) {
$configFileInfos[] = new SmartFileInfo($rectorRecipeFilePath);
}
return array_merge($configFileInfos, $setFileInfos);
}
}