mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 05:48:21 +01:00
[Nette] Add form dim access to standalone node control
This commit is contained in:
parent
d007ae0896
commit
d4f6c497a7
19
.github/workflows/standalone_run.yaml
vendored
19
.github/workflows/standalone_run.yaml
vendored
@ -17,18 +17,23 @@ jobs:
|
||||
coverage: none # disable xdebug, pcov
|
||||
extensions: "intl"
|
||||
|
||||
# Run standalone install in non-root package, ref https://github.com/rectorphp/rector/issues/732
|
||||
- run: |
|
||||
# wait till packagist gets information about new branches
|
||||
sleep 30
|
||||
# see https://github.com/rlespinasse/github-slug-action
|
||||
- name: Inject slug/short variables
|
||||
uses: rlespinasse/github-slug-action@v2.x
|
||||
|
||||
# wait till packagist gets information about new branches
|
||||
- run: sleep 30
|
||||
|
||||
- run:
|
||||
# 1. install locally
|
||||
mkdir test-paths
|
||||
cd test-paths
|
||||
|
||||
# 2. install rector to "rector-dir"
|
||||
mkdir rector-dir
|
||||
# get branch name see https://stackoverflow.com/a/61699863/1348344
|
||||
composer require rector/rector:dev-${GITHUB_REF#refs/heads/}#${GITHUB_SHA} -d rector-dir --no-progress --ansi
|
||||
composer require rector/rector:dev-${{ env.GITHUB_REF_SLUG_URL }}#${GITHUB_SHA} -d rector-dir --no-progress --ansi
|
||||
|
||||
# 3. download symfony demo to "symfony-demo-dir"
|
||||
mkdir symfony-demo-dir
|
||||
composer create-project symfony/symfony-demo symfony-demo-dir --dev --no-progress --ansi
|
||||
|
||||
@ -36,7 +41,7 @@ jobs:
|
||||
composer require doctrine/doctrine-fixtures-bundle -d symfony-demo-dir --no-progress --ansi
|
||||
composer dump-autoload --no-dev -d symfony-demo-dir --ansi
|
||||
|
||||
# 2. run an another project
|
||||
# 4. run an another project
|
||||
rector-dir/vendor/bin/rector --ansi
|
||||
|
||||
cd symfony-demo-dir
|
||||
|
@ -21,6 +21,8 @@ final class StaticEasyPrefixer
|
||||
'JMS\DiExtraBundle\Annotation\Inject',
|
||||
// part of public interface of configs.php
|
||||
'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator',
|
||||
// well, this is a function
|
||||
'Symfony\Component\DependencyInjection\Loader\Configurator\ref',
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -28,8 +28,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
|
||||
$parameters->set(Option::AUTOLOAD_PATHS, []);
|
||||
|
||||
$parameters->set('rector_recipe', []);
|
||||
|
||||
$parameters->set('project_type', 'proprietary');
|
||||
|
||||
$parameters->set('nested_chain_method_call_limit', 30);
|
||||
|
@ -3,6 +3,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\DeadCode\Rector\StaticCall\RemoveParentCallWithoutParentRector;
|
||||
use Rector\Nette\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector;
|
||||
use Rector\Nette\Rector\MethodCall\AddDatePickerToDateControlRector;
|
||||
use Rector\Nette\Rector\MethodCall\SetClassWithArgumentToSetFactoryRector;
|
||||
use Rector\Renaming\Rector\Class_\RenameClassRector;
|
||||
@ -11,6 +12,7 @@ use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigura
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$containerConfigurator->import(__DIR__ . '/nette-30-return-types.php');
|
||||
|
||||
$containerConfigurator->import(__DIR__ . '/nette-30-param-types.php');
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
@ -47,4 +49,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services->set(AddDatePickerToDateControlRector::class);
|
||||
|
||||
$services->set(SetClassWithArgumentToSetFactoryRector::class);
|
||||
|
||||
$services->set(ChangeFormArrayAccessToAnnotatedControlVariableRector::class);
|
||||
};
|
||||
|
@ -7,8 +7,6 @@ use Rector\PHPUnit\Rector\MethodCall\RemoveExpectAnyFromMockRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
# [configurable]
|
||||
# Rector\PHPUnit\Rector\Class_\ArrayArgumentInTestToDataProviderRector: null
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(RemoveExpectAnyFromMockRector::class);
|
||||
|
@ -4,14 +4,14 @@ declare(strict_types=1);
|
||||
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Set\ValueObject\Set;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set(Option::RECTOR_RECIPE, [
|
||||
# run "bin/rector create" to create a new Rector + tests from this config
|
||||
// run "bin/rector create" to create a new Rector + tests from this config
|
||||
'package' => 'Symfony',
|
||||
'name' => 'RemoveDefaultGetBlockPrefixRector',
|
||||
'node_types' => [
|
||||
@ -40,11 +40,11 @@ class TaskType extends AbstractType
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE,
|
||||
// e.g. link to RFC or headline in upgrade guide, 1 or more in the list
|
||||
'source' => [
|
||||
# e.g. link to RFC or headline in upgrade guide, 1 or more in the list
|
||||
'https://github.com/symfony/symfony/blob/3.4/UPGRADE-3.0.md',
|
||||
],
|
||||
# e.g. symfony30, target config to append this rector to
|
||||
'set' => Set::SYMFONY_30,
|
||||
// e.g. symfony30, target config to append this rector to
|
||||
'set' => SetList::SYMFONY_30,
|
||||
]);
|
||||
};
|
||||
|
@ -2,50 +2,62 @@
|
||||
|
||||
## 1. Configure a Rector Recipe in `rector.yaml`
|
||||
|
||||
```yaml
|
||||
# rector.yaml
|
||||
parameters:
|
||||
rector_recipe:
|
||||
# run "bin/rector create" to create a new Rector + tests from this config
|
||||
package: "Celebrity"
|
||||
name: "SplitToExplodeRector"
|
||||
node_types:
|
||||
# put the main node first, it is used to create the namespace
|
||||
- "Assign"
|
||||
description: "Removes unneeded $a = $a assignments"
|
||||
code_before: >
|
||||
<?php
|
||||
```php
|
||||
<?php
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$a = $a;
|
||||
}
|
||||
}
|
||||
declare(strict_types=1);
|
||||
|
||||
code_after: >
|
||||
<?php
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
source: # e.g. link to RFC or headline in upgrade guide, 1 or more in the list
|
||||
- ""
|
||||
set: "celebrity" # e.g. symfony30, target config to append this rector to
|
||||
$parameters->set(Option::RECTOR_RECIPE, [
|
||||
'package' => 'Celebrity',
|
||||
'name' => 'SplitToExplodeRector',
|
||||
'node_types' => [
|
||||
Assign::class,
|
||||
],
|
||||
'description' => 'Removes unneeded $a = $a assignments',
|
||||
'code_before' => <<<'CODE_SAMPLE'
|
||||
<?php
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$a = $a;
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE,
|
||||
'code_after' => <<<'CODE_SAMPLE'
|
||||
<?php
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE,
|
||||
// e.g. link to RFC or headline in upgrade guide, 1 or more in the list
|
||||
'source' => [
|
||||
],
|
||||
// e.g. symfony30, target config to append this rector to
|
||||
'set' => SetList::CELEBRITY,
|
||||
]);
|
||||
};
|
||||
```
|
||||
|
||||
## 2. Generate it
|
||||
|
||||
```bash
|
||||
vendor/bin/rector create-rector
|
||||
```
|
||||
There is also a shortcut command:
|
||||
```bash
|
||||
|
||||
# or for short
|
||||
vendor/bin/rector c
|
||||
```
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# All 528 Rectors Overview
|
||||
# All 530 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
@ -30,7 +30,7 @@
|
||||
- [MockistaToMockery](#mockistatomockery) (2)
|
||||
- [MysqlToMysqli](#mysqltomysqli) (4)
|
||||
- [Naming](#naming) (3)
|
||||
- [Nette](#nette) (12)
|
||||
- [Nette](#nette) (13)
|
||||
- [NetteCodeQuality](#nettecodequality) (1)
|
||||
- [NetteKdyby](#nettekdyby) (4)
|
||||
- [NetteTesterToPHPUnit](#nettetestertophpunit) (3)
|
||||
@ -59,6 +59,7 @@
|
||||
- [PhpSpecToPHPUnit](#phpspectophpunit) (7)
|
||||
- [Polyfill](#polyfill) (2)
|
||||
- [Privatization](#privatization) (7)
|
||||
- [RectorGenerator](#rectorgenerator) (1)
|
||||
- [RemovingStatic](#removingstatic) (4)
|
||||
- [Renaming](#renaming) (9)
|
||||
- [Restoration](#restoration) (7)
|
||||
@ -1039,7 +1040,7 @@ Complete missing 3rd argument in case `is_a()` function in case of strings
|
||||
- class: [`Rector\CodeQuality\Rector\Concat\JoinStringConcatRector`](/../master/rules/code-quality/src/Rector/Concat/JoinStringConcatRector.php)
|
||||
- [test fixtures](/../master/rules/code-quality/tests/Rector/Concat/JoinStringConcatRector/Fixture)
|
||||
|
||||
Joins concat of 2 strings
|
||||
Joins concat of 2 strings, unless the lenght is too long
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
@ -5004,7 +5005,8 @@ Rename property and method param to match its type
|
||||
Rename variable to match get method name
|
||||
|
||||
```diff
|
||||
class SomeClass {
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
- $a = $this->getRunner();
|
||||
@ -5062,6 +5064,33 @@ Nextras/Form upgrade of addDatePicker method call to DateControl assign
|
||||
|
||||
<br><br>
|
||||
|
||||
### `ChangeFormArrayAccessToAnnotatedControlVariableRector`
|
||||
|
||||
- class: [`Rector\Nette\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector`](/../master/rules/nette/src/Rector/ArrayDimFetch/ChangeFormArrayAccessToAnnotatedControlVariableRector.php)
|
||||
- [test fixtures](/../master/rules/nette/tests/Rector/ArrayDimFetch/ChangeFormArrayAccessToAnnotatedControlVariableRector/Fixture)
|
||||
|
||||
Change array access magic on `$form` to explicit standalone typed variable
|
||||
|
||||
```diff
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$form = new Form();
|
||||
$this->addText('email', 'Email');
|
||||
|
||||
- $form['email']->value = 'hey@hi.hello';
|
||||
+ /** @var \Nette\Forms\Controls\BaseControl $emailControl */
|
||||
+ $emailControl = $form['email'];
|
||||
+ $emailControl->value = 'hey@hi.hello';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
### `ContextGetByTypeToConstructorInjectionRector`
|
||||
|
||||
- class: [`Rector\Nette\Rector\MethodCall\ContextGetByTypeToConstructorInjectionRector`](/../master/rules/nette/src/Rector/MethodCall/ContextGetByTypeToConstructorInjectionRector.php)
|
||||
@ -8032,13 +8061,11 @@ Changes `$this->call()` to static method to static call
|
||||
Ensure variable variables are wrapped in curly braces
|
||||
|
||||
```diff
|
||||
function run($foo)
|
||||
{
|
||||
- // Valid in PHP 5 only
|
||||
function run($foo)
|
||||
{
|
||||
- global $$foo->bar;
|
||||
+ // Valid in PHP 5 and 7
|
||||
+ global ${$foo->bar};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br><br>
|
||||
@ -9560,6 +9587,25 @@ Privatize local-only property to private property
|
||||
|
||||
<br><br>
|
||||
|
||||
## RectorGenerator
|
||||
|
||||
### `AddNewServiceToSymfonyPhpConfigRector`
|
||||
|
||||
- class: [`Rector\RectorGenerator\Rector\Closure\AddNewServiceToSymfonyPhpConfigRector`](/../master/packages/rector-generator/src/Rector/Closure/AddNewServiceToSymfonyPhpConfigRector.php)
|
||||
|
||||
Adds a new `$services->set(...)` call to PHP Config
|
||||
|
||||
```diff
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
+ $services->set(AddNewServiceToSymfonyPhpConfigRector::class);
|
||||
};
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
## RemovingStatic
|
||||
|
||||
### `NewUniqueObjectToEntityFactoryRector`
|
||||
|
@ -83,6 +83,11 @@ final class PhpDocInfo
|
||||
*/
|
||||
private $paramPhpDocNodeFactory;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isSingleLine = false;
|
||||
|
||||
/**
|
||||
* @param mixed[] $tokens
|
||||
*/
|
||||
@ -434,6 +439,16 @@ final class PhpDocInfo
|
||||
return $throwsClasses;
|
||||
}
|
||||
|
||||
public function makeSingleLined(): void
|
||||
{
|
||||
$this->isSingleLine = true;
|
||||
}
|
||||
|
||||
public function isSingleLine(): bool
|
||||
{
|
||||
return $this->isSingleLine;
|
||||
}
|
||||
|
||||
private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
|
||||
{
|
||||
/** @var AttributeAwareParamTagValueNode $paramTagValue */
|
||||
|
@ -139,7 +139,12 @@ final class DocBlockManipulator
|
||||
{
|
||||
// new node, needs to be reparsed
|
||||
if ($phpDocInfo->isNewNode()) {
|
||||
return (string) $phpDocInfo->getPhpDocNode();
|
||||
$docContent = (string) $phpDocInfo->getPhpDocNode();
|
||||
if (! $phpDocInfo->isSingleLine()) {
|
||||
return $docContent;
|
||||
}
|
||||
|
||||
return $this->inlineDocContent($docContent);
|
||||
}
|
||||
|
||||
return $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo);
|
||||
@ -191,4 +196,11 @@ final class DocBlockManipulator
|
||||
{
|
||||
return Strings::replace($content, '#(\s|\*)+#');
|
||||
}
|
||||
|
||||
private function inlineDocContent(string $docContent): string
|
||||
{
|
||||
$docContent = Strings::replace($docContent, "#\n \* #", ' ');
|
||||
|
||||
return Strings::replace($docContent, "#\n \*\/$#", ' */');
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\RectorGenerator\Rector\Closure\AddNewServiceToSymfonyPhpConfigRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
use function Symfony\Component\DependencyInjection\Loader\Configurator\ref;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
@ -10,12 +13,20 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services->defaults()
|
||||
->public()
|
||||
->autowire()
|
||||
->autoconfigure();
|
||||
->autoconfigure()
|
||||
->bind(AddNewServiceToSymfonyPhpConfigRector::class, ref(AddNewServiceToSymfonyPhpConfigRector::class));
|
||||
|
||||
$services->load('Rector\RectorGenerator\\', __DIR__ . '/../src')
|
||||
->exclude([__DIR__ . '/../src/Exception/*', __DIR__ . '/../src/ValueObject/*']);
|
||||
->exclude([
|
||||
__DIR__ . '/../src/Exception/*',
|
||||
__DIR__ . '/../src/ValueObject/*',
|
||||
__DIR__ . '/../src/Rector/*',
|
||||
]);
|
||||
|
||||
$services->set(AddNewServiceToSymfonyPhpConfigRector::class)
|
||||
->autowire(false);
|
||||
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set('rector_recipe', []);
|
||||
$parameters->set(Option::RECTOR_RECIPE, []);
|
||||
};
|
||||
|
@ -144,10 +144,10 @@ final class CreateRectorCommand extends Command
|
||||
return ShellCode::SUCCESS;
|
||||
}
|
||||
|
||||
$this->generateFiles($templateFileInfos, $templateVariables, $configuration);
|
||||
|
||||
$this->configFilesystem->appendRectorServiceToSet($configuration, $templateVariables);
|
||||
|
||||
$this->generateFiles($templateFileInfos, $templateVariables, $configuration);
|
||||
|
||||
$this->printSuccess($configuration->getName());
|
||||
|
||||
return ShellCode::SUCCESS;
|
||||
|
@ -6,7 +6,12 @@ namespace Rector\RectorGenerator\Config;
|
||||
|
||||
use Nette\Utils\FileSystem;
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\NodeTraverser;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use Rector\Core\PhpParser\Parser\Parser;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\RectorGenerator\Rector\Closure\AddNewServiceToSymfonyPhpConfigRector;
|
||||
use Rector\RectorGenerator\TemplateFactory;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
|
||||
@ -22,9 +27,31 @@ final class ConfigFilesystem
|
||||
*/
|
||||
private $templateFactory;
|
||||
|
||||
public function __construct(TemplateFactory $templateFactory)
|
||||
{
|
||||
/**
|
||||
* @var Parser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* @var AddNewServiceToSymfonyPhpConfigRector
|
||||
*/
|
||||
private $addNewServiceToSymfonyPhpConfigRector;
|
||||
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
private $betterStandardPrinter;
|
||||
|
||||
public function __construct(
|
||||
TemplateFactory $templateFactory,
|
||||
Parser $parser,
|
||||
AddNewServiceToSymfonyPhpConfigRector $addNewServiceToSymfonyPhpConfigRector,
|
||||
BetterStandardPrinter $betterStandardPrinter
|
||||
) {
|
||||
$this->templateFactory = $templateFactory;
|
||||
$this->parser = $parser;
|
||||
$this->addNewServiceToSymfonyPhpConfigRector = $addNewServiceToSymfonyPhpConfigRector;
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -36,20 +63,40 @@ final class ConfigFilesystem
|
||||
return;
|
||||
}
|
||||
|
||||
if (! file_exists($configuration->getSetConfig())) {
|
||||
return;
|
||||
}
|
||||
$setConfigFileInfo = $configuration->getSetConfig();
|
||||
$setFileContents = $setConfigFileInfo->getContents();
|
||||
|
||||
$setConfigContent = FileSystem::read($configuration->getSetConfig());
|
||||
|
||||
// already added
|
||||
// already added?
|
||||
$rectorFqnName = $this->templateFactory->create(self::RECTOR_FQN_NAME_PATTERN, $templateVariables);
|
||||
if (Strings::contains($setConfigContent, $rectorFqnName)) {
|
||||
if (Strings::contains($setFileContents, $rectorFqnName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotImplementedYetException('Add *.php config rule append support');
|
||||
// 1. parse the file
|
||||
$setConfigNodes = $this->parser->parseFileInfo($setConfigFileInfo);
|
||||
|
||||
// FileSystem::write($configuration->getSetConfig(), $newSetConfigContent);
|
||||
// 2. add the set() call
|
||||
$this->decorateNamesToFullyQualified($setConfigNodes);
|
||||
|
||||
$nodeTraverser = new NodeTraverser();
|
||||
|
||||
$this->addNewServiceToSymfonyPhpConfigRector->setRectorClass($rectorFqnName);
|
||||
$nodeTraverser->addVisitor($this->addNewServiceToSymfonyPhpConfigRector);
|
||||
$setConfigNodes = $nodeTraverser->traverse($setConfigNodes);
|
||||
|
||||
// 3. print the content back to file
|
||||
$changedSetConfigContent = $this->betterStandardPrinter->prettyPrintFile($setConfigNodes);
|
||||
FileSystem::write($setConfigFileInfo->getRealPath(), $changedSetConfigContent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[] $nodes
|
||||
*/
|
||||
private function decorateNamesToFullyQualified(array $nodes): void
|
||||
{
|
||||
// decorate nodes with names first
|
||||
$nameResolverNodeTraverser = new NodeTraverser();
|
||||
$nameResolverNodeTraverser->addVisitor(new NameResolver());
|
||||
$nameResolverNodeTraverser->traverse($nodes);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorGenerator\Rector\Closure;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class AddNewServiceToSymfonyPhpConfigRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $rectorClass;
|
||||
|
||||
public function setRectorClass(string $rectorClass): void
|
||||
{
|
||||
$this->rectorClass = $rectorClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Closure::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if ($this->rectorClass === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isPhpConfigClosure($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodCall = $this->createServicesSetMethodCall($this->rectorClass);
|
||||
$node->stmts[] = new Expression($methodCall);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Adds a new $services->set(...) call to PHP Config', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
};
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(AddNewServiceToSymfonyPhpConfigRector::class);
|
||||
};
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
private function isPhpConfigClosure(Closure $closure): bool
|
||||
{
|
||||
if (count($closure->params) !== 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$onlyParam = $closure->params[0];
|
||||
if ($onlyParam->type === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isName(
|
||||
$onlyParam->type,
|
||||
'Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator'
|
||||
);
|
||||
}
|
||||
|
||||
private function createServicesSetMethodCall(string $className): MethodCall
|
||||
{
|
||||
$servicesVariable = new Variable('services');
|
||||
$referenceClassConstFetch = new ClassConstFetch(new FullyQualified($className), new Identifier('class'));
|
||||
$args = [new Arg($referenceClassConstFetch)];
|
||||
|
||||
return new MethodCall($servicesVariable, 'set', $args);
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ namespace Rector\RectorGenerator\ValueObject;
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\Util\StaticRectorStrings;
|
||||
use Symplify\SetConfigResolver\ValueObject\Set;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class Configuration
|
||||
{
|
||||
@ -158,13 +159,13 @@ final class Configuration
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function getSetConfig(): ?string
|
||||
public function getSetConfig(): ?SmartFileInfo
|
||||
{
|
||||
if ($this->set === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->set->getSetFileInfo()->getRealPath();
|
||||
return $this->set->getSetFileInfo();
|
||||
}
|
||||
|
||||
public function isPhpSnippet(): bool
|
||||
|
11
phpstan.neon
11
phpstan.neon
@ -369,5 +369,16 @@ parameters:
|
||||
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'
|
||||
|
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\Rector\ArrayDimFetch;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\Assign;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
||||
/**
|
||||
* @see \Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector\ChangeFormArrayAccessToAnnotatedControlVariableRectorTest
|
||||
*/
|
||||
final class ChangeFormArrayAccessToAnnotatedControlVariableRector extends AbstractRector
|
||||
{
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Change array access magic on $form to explicit standalone typed variable', [
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$form = new Form();
|
||||
$this->addText('email', 'Email');
|
||||
|
||||
$form['email']->value = 'hey@hi.hello';
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$form = new Form();
|
||||
$this->addText('email', 'Email');
|
||||
|
||||
/** @var \Nette\Forms\Controls\BaseControl $emailControl */
|
||||
$emailControl = $form['email'];
|
||||
$emailControl->value = 'hey@hi.hello';
|
||||
}
|
||||
}
|
||||
PHP
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ArrayDimFetch::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ArrayDimFetch $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
if (! $node->var instanceof Variable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isObjectType($node->var, 'Nette\Application\UI\Form')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $node->dim instanceof String_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$inputName = $this->getValue($node->dim);
|
||||
$controlName = $inputName . 'Control';
|
||||
|
||||
$controlVariableToFormDimFetchAssign = new Assign(new Variable($controlName), clone $node);
|
||||
$assignExpression = new Expression($controlVariableToFormDimFetchAssign);
|
||||
|
||||
$this->addVarTag($controlVariableToFormDimFetchAssign, $assignExpression, $controlName);
|
||||
|
||||
$this->addNodeBeforeNode($assignExpression, $node);
|
||||
|
||||
return new Variable($controlName);
|
||||
}
|
||||
|
||||
private function addVarTag(Assign $assign, Expression $assignExpression, string $controlName): PhpDocInfo
|
||||
{
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($assignExpression);
|
||||
$identifierTypeNode = new IdentifierTypeNode('\Nette\Forms\Controls\BaseControl');
|
||||
|
||||
$varTagValueNode = new VarTagValueNode($identifierTypeNode, '$' . $controlName, '');
|
||||
$phpDocInfo->addTagValueNode($varTagValueNode);
|
||||
$phpDocInfo->makeSingleLined();
|
||||
|
||||
$assign->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);
|
||||
|
||||
return $phpDocInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\Nette\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ChangeFormArrayAccessToAnnotatedControlVariableRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return ChangeFormArrayAccessToAnnotatedControlVariableRector::class;
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$form = new Form();
|
||||
$this->addText('email', 'Email');
|
||||
$form['email']->value = 'hey@hi.hello';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Nette\Tests\Rector\ArrayDimFetch\ChangeFormArrayAccessToAnnotatedControlVariableRector\Fixture;
|
||||
|
||||
use Nette\Application\UI\Form;
|
||||
|
||||
class SomePresenter
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
$form = new Form();
|
||||
$this->addText('email', 'Email');
|
||||
/** @var \Nette\Forms\Controls\BaseControl $emailControl */
|
||||
$emailControl = $form['email'];
|
||||
$emailControl->value = 'hey@hi.hello';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Loading…
x
Reference in New Issue
Block a user