Merge pull request #28 from TomasVotruba/nette-forms-set-required

[Nette] forms setRequired(false)
This commit is contained in:
Tomáš Votruba 2017-09-06 22:46:11 +02:00 committed by GitHub
commit 645d92dba9
29 changed files with 822 additions and 72 deletions

View File

@ -3,7 +3,7 @@
[![Build Status](https://img.shields.io/travis/TomasVotruba/Rector/master.svg?style=flat-square)](https://travis-ci.org/TomasVotruba/Rector)
[![Coverage Status](https://img.shields.io/coveralls/TomasVotruba/Rector/master.svg?style=flat-square)](https://coveralls.io/github/TomasVotruba/Rector?branch=master)
This tool will *reconstruct* (change) your code - **run it only in a new clean git branch**.
This tool will **upgrade your application** for you.
## All Reconstructors
@ -15,14 +15,12 @@ This tool will *reconstruct* (change) your code - **run it only in a new clean g
- `HtmlAddMethodRector`
- `NetteObjectToSmartTraitRector`
- `RemoveConfiguratorConstantsRector`
- and many others
### [Symfony](https://github.com/symfony/)
- `NamedServicesToConstructorNodeTraverser`
### Abstract to use
- `AbstractChangeMethodNameRector`
- and many others
## Install

View File

@ -24,13 +24,15 @@
"autoload": {
"psr-4": {
"Rector\\": "src",
"Rector\\NodeTypeResolver\\": "packages/NodeTypeResolver/src"
"Rector\\NodeTypeResolver\\": "packages/NodeTypeResolver/src",
"Rector\\TriggerExtractor\\": "packages/TriggerExtractor/src"
}
},
"autoload-dev": {
"psr-4": {
"Rector\\Tests\\": "tests",
"Rector\\NodeTypeResolver\\Tests\\": "packages/NodeTypeResolver/tests"
"Rector\\NodeTypeResolver\\Tests\\": "packages/NodeTypeResolver/tests",
"Rector\\TriggerExtractor\\Tests\\": "packages/TriggerExtractor/tests"
}
},
"scripts": {

View File

@ -0,0 +1,52 @@
# Node Type Resolver
This package detects `class`, type `class_node` and `$variable` or `$this->property` types and adds them to all relevant nodes.
Class type is added to all nodes inside it, so you always now where you are.
Anonymous classes are skipped.
## How it works?
1. Traverse all nodes
2. Detect variable assigns, property use, method arguments
3. Resolve types
4. Add them via `$node->setAttribute('type', $type);` to ever node
## How it helps you?
You can get `class`
```php
$class = (string) $node->getAttribute('class');
if (Strings::endsWith($class, 'Command')) {
// we are in Command class
}
// to be sure it's console command
/** @var PhpParser\Node\Name\FullyQualified $fqnName */
$classNode = $node->getAttribute('class_node');
$fqnName = $classNode->extends->getAttribute('resolvedName');
if ($fqnName->toString() === 'Symfony\Component\Console\Command') {
// we are sure it's child of Symfony\Console Command class
}
```
or `type` attribute:
```php
/** @var string $type */
$type = $node->var->getAttribute('type');
if ($type === 'Nette\Application\UI\Form') {
// this is Nette\Application\UI\Form variable
}
```
...in any Rector you create.

View File

@ -0,0 +1,49 @@
<?php declare(strict_types=1);
namespace Rector\NodeTypeResolver\NodeVisitor;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\NodeVisitorAbstract;
/**
* Add attribute 'class' with current class name.
*/
final class ClassResolver extends NodeVisitorAbstract
{
/**
* @var string
*/
private const CLASS_ATTRIBUTE = 'class';
/**
* @var string
*/
private const CLASS_NODE_ATTRIBUTE = 'class_node';
/**
* @var Class_|null
*/
private $classNode;
/**
* @param Node[] $nodes
*/
public function beforeTraverse(array $nodes): void
{
$this->classNode = null;
}
public function enterNode(Node $node): void
{
// detect only first "class" to prevent anonymous classes interference
if ($this->classNode === null && $node instanceof Class_) {
$this->classNode = $node;
}
if ($this->classNode) {
$node->setAttribute(self::CLASS_NODE_ATTRIBUTE, $this->classNode);
$node->setAttribute(self::CLASS_ATTRIBUTE, $this->classNode->namespacedName->toString());
}
}
}

View File

@ -17,7 +17,7 @@ use Rector\NodeTypeResolver\TypeContext;
/**
* Inspired by https://github.com/nikic/PHP-Parser/blob/9373a8e9f551516bc8e42aedeacd1b4f635d27fc/lib/PhpParser/NodeVisitor/NameResolver.php.
*/
final class ClassLikeTypeResolver extends NodeVisitorAbstract
final class TypeResolver extends NodeVisitorAbstract
{
/**
* @var string

View File

@ -0,0 +1,25 @@
# Trigger Extractor
This package extracts `trigger_error(*, E_USER_DEPRECATED)` from the code.
It helps to generated automate rectors.
## How it works?
1. Just read the message
2. Detect what code should be change into what code
3. Create rectors that will do the work
## How it helps you?
Instead of using your eyes and brain to read `CHANGELOG.md` or `UPGRADE-x.md`, just run it over directory of framework you use:
```bash
vendor/bin/rector extract-deprecations vendor/nette
```
It will show you what changed and how.
Moreover, it will change the code for you.

View File

@ -0,0 +1,55 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor\Console\Command;
use Rector\TriggerExtractor\TriggerExtractor;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ExtractCommand extends Command
{
/**
* @var string
*/
private const NAME = 'extract-deprecations';
/**
* @var string
*/
private const ARGUMENT_SOURCE_NAME = 'source';
/**
* @var TriggerExtractor
*/
private $triggerExtractor;
public function __construct(TriggerExtractor $triggerExtractor)
{
$this->triggerExtractor = $triggerExtractor;
parent::__construct();
}
protected function configure(): void
{
$this->setName(self::NAME);
$this->setDescription('Extract deprecation notes from PHP files in directory(ies).');
$this->addArgument(
self::ARGUMENT_SOURCE_NAME,
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'One or more directory to be checked.'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$source = $input->getArgument(self::ARGUMENT_SOURCE_NAME);
$this->triggerExtractor->scanDirectories($source);
// write found deprecations...
return 0;
}
}

View File

@ -0,0 +1,24 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor\Deprecation;
final class DeprecationCollector
{
/**
* @var mixed[]
*/
private $deprecations = [];
public function addDeprecation(string $deprecation): void
{
$this->deprecations[] = $deprecation;
}
/**
* @return mixed[]
*/
public function getDeprecations(): array
{
return $this->deprecations;
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor\NodeVisitor;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\NodeVisitorAbstract;
use Rector\TriggerExtractor\Deprecation\DeprecationCollector;
use Rector\TriggerExtractor\TriggerMessageResolver;
final class DeprecationDetector extends NodeVisitorAbstract
{
/**
* @var DeprecationCollector
*/
private $deprecationCollector;
/**
* @var TriggerMessageResolver
*/
private $triggerMessageResolver;
public function __construct(
DeprecationCollector $deprecationCollector,
TriggerMessageResolver $triggerMessageResolver
) {
$this->deprecationCollector = $deprecationCollector;
$this->triggerMessageResolver = $triggerMessageResolver;
}
public function enterNode(Node $node): void
{
if (! $this->isTriggerErrorUserDeprecated($node)) {
return;
}
/** @var FuncCall $node */
$deprecation = $this->triggerMessageResolver->resolve($node->args[0]->value);
$this->deprecationCollector->addDeprecation($deprecation);
}
/**
* This detects: "trigger_error(<some-content>, E_USER_DEPREDCATED)";
*/
private function isTriggerErrorUserDeprecated(Node $node): bool
{
if (! $this->isFunctionWithName($node, 'trigger_error')) {
return false;
}
/** @var FuncCall $node */
if (count($node->args) !== 2) {
return false;
}
/** @var Arg $secondArgumentNode */
$secondArgumentNode = $node->args[1];
if (! $secondArgumentNode->value instanceof ConstFetch) {
return false;
}
/** @var ConstFetch $constFetchNode */
$constFetchNode = $secondArgumentNode->value;
return $constFetchNode->name->toString() === 'E_USER_DEPRECATED';
}
private function isFunctionWithName(Node $node, string $name): bool
{
if (! $node instanceof FuncCall) {
return false;
}
if (! $node->name instanceof Name) {
return false;
}
return $node->name->toString() === $name;
}
}

View File

@ -0,0 +1,73 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor;
use Rector\Contract\Parser\ParserInterface;
use Rector\NodeTraverser\MainNodeTraverser;
use Rector\NodeTraverser\StandaloneTraverseNodeTraverser;
use Rector\TriggerExtractor\NodeVisitor\DeprecationDetector;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
final class TriggerExtractor
{
/**
* @var ParserInterface
*/
private $parser;
/**
* @var MainNodeTraverser
*/
private $mainNodeTraverser;
/**
* @var StandaloneTraverseNodeTraverser
*/
private $standaloneTraverseNodeTraverser;
public function __construct(
ParserInterface $parser,
MainNodeTraverser $mainNodeTraverser,
DeprecationDetector $deprecationDetector,
StandaloneTraverseNodeTraverser $standaloneTraverseNodeTraverser
) {
$this->parser = $parser;
$this->standaloneTraverseNodeTraverser = $standaloneTraverseNodeTraverser;
$this->mainNodeTraverser = $mainNodeTraverser;
$this->mainNodeTraverser->addVisitor($deprecationDetector);
}
/**
* @param string[] $directories
*/
public function scanDirectories(array $directories): void
{
$files = $this->findPhpFilesInDirectories($directories);
foreach ($files as $file) {
$nodes = $this->parser->parseFile($file->getRealPath());
// this completes parent & child nodes, types and classses
$this->standaloneTraverseNodeTraverser->traverse($nodes);
$this->mainNodeTraverser->traverse($nodes);
}
}
/**
* @todo duplicated method to
* @see \Rector\Console\Command\ReconstructCommand, extract to class
*
* @param string[] $directories
* @return SplFileInfo[] array
*/
private function findPhpFilesInDirectories(array $directories): array
{
$finder = Finder::create()
->files()
->name('*.php')
->in($directories);
return iterator_to_array($finder->getIterator());
}
}

View File

@ -0,0 +1,81 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor;
use Exception;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\BinaryOp\Concat;
use PhpParser\Node\Scalar\MagicConst\Method;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\ClassMethod;
final class TriggerMessageResolver
{
/**
* Probably resolve by recursion, similar too
* @see \Rector\NodeTypeResolver\NodeVisitor\TypeResolver::__construct()
*/
public function resolve(Node $node): string
{
$message = '';
if ($node instanceof Concat) {
$message .= $this->processConcatNode($node->left);
$message .= $this->processConcatNode($node->right);
}
return $message;
}
private function processConcatNode(Node $node): string
{
if ($node instanceof Method) {
$classMethodNode = $this->findParentOfType($node, ClassMethod::class);
return $node->getAttribute('class') . '::' . $classMethodNode->name->name;
}
if ($node instanceof String_) {
$message = $node->value; // complet class to local methods
return $this->completeClassToLocalMethods($message, $node->getAttribute('class'));
}
throw new Exception(sprintf(
'Not implemented yet. Go to "%s::%s()" and add check for "%s" node.',
__CLASS__,
__METHOD__,
get_class($node)
));
}
private function findParentOfType(Node $node, string $type): Node
{
$parentNode = $node->getAttribute('parent');
while (! is_a($parentNode, $type, true)) {
$parentNode = $parentNode->getAttribute('parent');
}
return $parentNode;
}
private function completeClassToLocalMethods(string $message, string $class): string
{
$completeMessage = '';
$words = explode(' ', $message);
foreach ($words as $word) {
// is method()
if (Strings::endsWith($word, '()') && strlen($word) > 2) {
// doesn't include class in the beggning
if (! Strings::startsWith($word, $class)) {
$word = $class . '::' . $word;
}
}
$completeMessage .= ' ' . $word;
}
return trim($completeMessage);
}
}

View File

@ -0,0 +1,7 @@
services:
_defaults:
autowire: true
# PSR-4 autodiscovery
Rector\TriggerExtractor\:
resource: '../../src'

View File

@ -0,0 +1,37 @@
<?php
/**
* This file is part of the Nette Framework (https://nette.org)
* Copyright (c) 2004 David Grudl (https://davidgrudl.com)
*/
declare(strict_types=1);
namespace Nette\DI;
use Nette;
final class Definition
{
/**
* @return static
* @deprecated
*/
public function setClass(?string $type): self
{
if (func_num_args() > 1) {
@trigger_error(__METHOD__ . '() second parameter $args is deprecated, use setFactory()', E_USER_DEPRECATED);
if ($args = func_get_arg(1)) {
$this->setFactory($type, $args);
}
}
return $this;
}
public function setInject(bool $state = true): self
{
@trigger_error(__METHOD__ . "() is deprecated, use addTag('inject')", E_USER_DEPRECATED);
return $this->addTag(InjectExtension::TAG_INJECT, $state);
}
}

View File

@ -0,0 +1,42 @@
<?php declare(strict_types=1);
namespace Rector\TriggerExtractor\Tests;
use Rector\Tests\AbstractContainerAwareTestCase;
use Rector\TriggerExtractor\Deprecation\DeprecationCollector;
use Rector\TriggerExtractor\TriggerExtractor;
final class TriggerExtractorTest extends AbstractContainerAwareTestCase
{
/**
* @var TriggerExtractor
*/
private $triggerExtractor;
/**
* @var DeprecationCollector
*/
private $deprecationCollector;
protected function setUp(): void
{
$this->triggerExtractor = $this->container->get(TriggerExtractor::class);
$this->deprecationCollector = $this->container->get(DeprecationCollector::class);
}
public function test(): void
{
$this->triggerExtractor->scanDirectories([__DIR__ . '/TriggerExtractorSource']);
$deprecations = $this->deprecationCollector->getDeprecations();
$this->assertCount(2, $deprecations);
$setClassToSetFacoryDeprecation = $deprecations[0];
$this->assertSame(
'Nette\DI\Definition::setClass() second parameter $args is deprecated,'
. ' use Nette\DI\Definition::setFactory()',
$setClassToSetFacoryDeprecation
);
}
}

View File

@ -3,4 +3,6 @@ parameters:
- '#Internal error#'
- '#Undefined variable: \$possibleAutoloadPaths#'
- '#Undefined variable: \$container#'
- '#Undefined variable: \$application#'
- '#Undefined variable: \$application#'
excludes_analyse:
- *packages/TriggerExtractor/tests/TriggerExtractorSource/Definition.php

View File

@ -15,7 +15,7 @@ final class ReconstructCommand extends Command
/**
* @var string
*/
private const NAME = 'processClass';
private const NAME = 'process';
/**
* @var string

View File

@ -0,0 +1,52 @@
<?php declare(strict_types=1);
namespace Rector\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
final class ClassResolver
{
/**
* @var Class_|null
*/
private $classNode;
/**
* @param Node[] $nodes
*/
public function resolveFromNodes(array $nodes): void
{
$this->classNode = null;
foreach ($nodes as $node) {
if ($node instanceof Class_) {
$this->classNode = $node;
break;
}
}
}
public function getClassName(): string
{
if ($this->classNode === null) {
return '';
}
return $this->classNode->namespacedName->toString();
}
public function getParentClassName(): string
{
if ($this->classNode === null) {
return '';
}
$parentClass = $this->classNode->extends;
/** @var Node\Name\FullyQualified $fqnParentClassName */
$fqnParentClassName = $parentClass->getAttribute('resolvedName');
return $fqnParentClassName->toString();
}
}

View File

@ -3,10 +3,23 @@
namespace Rector\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
final class MethodCallAnalyzer
{
/**
* @param string[] $methodsNames
*/
public function isMethodCallTypeAndMethods(Node $node, string $type, array $methodsNames): bool
{
if (! $this->isMethodCallType($node, $type)) {
return false;
}
return in_array((string) $node->name, $methodsNames, true);
}
/**
* @param string[] $methodNames
*/
@ -28,6 +41,20 @@ final class MethodCallAnalyzer
return false;
}
private function isMethodCallType(Node $node, string $type): bool
{
if (! $node instanceof MethodCall) {
return false;
}
$variableType = $this->findVariableType($node);
if ($variableType !== $type) {
return false;
}
return true;
}
private function isStaticMethodCallType(Node $node, string $type): bool
{
if (! $node instanceof StaticCall) {
@ -40,4 +67,15 @@ final class MethodCallAnalyzer
return true;
}
private function findVariableType(MethodCall $methodCallNode): string
{
$varNode = $methodCallNode->var;
while ($varNode->getAttribute('type') === null) {
$varNode = $varNode->var;
}
return (string) $varNode->getAttribute('type');
}
}

View File

@ -3,6 +3,7 @@
namespace Rector\NodeFactory;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\ClassConstFetch;
@ -35,6 +36,11 @@ final class NodeFactory
return new ConstFetch(new Name('null'));
}
public function createFalseConstant(): ConstFetch
{
return new ConstFetch(new Name('false'));
}
public function createClassConstant(string $className, string $constantName): ClassConstFetch
{
$classNameNode = new FullyQualified($className);
@ -83,4 +89,18 @@ final class NodeFactory
'kind' => Array_::KIND_SHORT,
]);
}
/**
* @param mixed[] $arguments
* @return Arg[]
*/
public function createArgs(array $arguments): array
{
$args = [];
foreach ($arguments as $argument) {
$args[] = new Arg($argument);
}
return $args;
}
}

View File

@ -10,11 +10,14 @@ abstract class AbstractChangeParentClassRector extends AbstractRector
{
public function isCandidate(Node $node): bool
{
if (! $node instanceof Class_) {
if (! $node instanceof Class_ || $node->extends === null) {
return false;
}
return $this->getParentClassName() === $this->getOldClassName();
/** @var FullyQualified $fqnName */
$fqnName = $node->extends->getAttribute('resolvedName');
return $fqnName->toString() === $this->getOldClassName();
}
/**

View File

@ -3,7 +3,6 @@
namespace Rector\Rector;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PhpParser\NodeTraverser;
use PhpParser\NodeVisitorAbstract;
use Rector\Contract\Deprecation\DeprecationInterface;
@ -11,29 +10,6 @@ use Rector\Contract\Rector\RectorInterface;
abstract class AbstractRector extends NodeVisitorAbstract implements DeprecationInterface, RectorInterface
{
/**
* @var Class_|null
*/
protected $classNode;
/**
* @param Node[] $nodes
* @return null|Node[]
*/
public function beforeTraverse(array $nodes): ?array
{
$this->classNode = null;
foreach ($nodes as $node) {
if ($node instanceof Class_) {
$this->classNode = $node;
break;
}
}
return null;
}
/**
* @return null|int|Node
*/
@ -49,27 +25,4 @@ abstract class AbstractRector extends NodeVisitorAbstract implements Deprecation
return null;
}
protected function getClassName(): string
{
if ($this->classNode === null) {
return '';
}
return $this->classNode->namespacedName->toString();
}
protected function getParentClassName(): string
{
if ($this->classNode === null) {
return '';
}
$parentClass = $this->classNode->extends;
/** @var Node\Name\FullyQualified $fqnParentClassName */
$fqnParentClassName = $parentClass->getAttribute('resolvedName');
return $fqnParentClassName->toString();
}
}

View File

@ -0,0 +1,84 @@
<?php declare(strict_types=1);
namespace Rector\Rector\Contrib\Nette;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use Rector\Deprecation\SetNames;
use Rector\NodeAnalyzer\MethodCallAnalyzer;
use Rector\NodeFactory\NodeFactory;
use Rector\Rector\AbstractRector;
/**
* Covers https://forum.nette.org/cs/26672-missing-setrequired-true-false-on-field-abc-in-form
*/
final class FormSetRequiredRector extends AbstractRector
{
/**
* @var string
*/
public const FORM_CLASS = 'Nette\Application\UI\Form';
/**
* @var MethodCallAnalyzer
*/
private $methodCallAnalyzer;
/**
* @var NodeFactory
*/
private $nodeFactory;
public function __construct(MethodCallAnalyzer $methodCallAnalyzer, NodeFactory $nodeFactory)
{
$this->methodCallAnalyzer = $methodCallAnalyzer;
$this->nodeFactory = $nodeFactory;
}
public function getSetName(): string
{
return SetNames::NETTE;
}
public function sinceVersion(): float
{
return 2.4;
}
public function isCandidate(Node $node): bool
{
if (! $this->methodCallAnalyzer->isMethodCallTypeAndMethods($node, self::FORM_CLASS, ['addCondition'])) {
return false;
}
/** @var MethodCall $node */
if (count($node->args) !== 1) {
return false;
}
$arg = $node->args[0];
if (! $arg->value instanceof ClassConstFetch) {
return false;
}
if ($arg->value->class->getAttribute('type') !== self::FORM_CLASS) {
return false;
}
return $arg->value->name->name === 'FILLED';
}
/**
* @param MethodCall $node
*/
public function refactor(Node $node): ?Node
{
$node->name->name = 'setRequired';
$node->args = $this->nodeFactory->createArgs([
$this->nodeFactory->createFalseConstant(),
]);
return $node;
}
}

View File

@ -3,6 +3,7 @@
namespace Rector\Rector\Contrib\Nette;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use Rector\Builder\StatementGlue;
use Rector\Deprecation\SetNames;
@ -52,15 +53,14 @@ final class NetteObjectToSmartTraitRector extends AbstractRector
public function isCandidate(Node $node): bool
{
if (! $node instanceof Class_) {
if (! $node instanceof Class_ || $node->extends === null) {
return false;
}
if (! $node->extends) {
return false;
}
/** @var FullyQualified $fqnName */
$fqnName = $node->extends->getAttribute('resolvedName');
return $this->getParentClassName() === self::PARENT_CLASS;
return $fqnName->toString() === self::PARENT_CLASS;
}
/**

View File

@ -87,7 +87,9 @@ final class CommandToConstructorInjectionRector extends AbstractRector
public function isCandidate(Node $node): bool
{
if (! Strings::endsWith($this->getClassName(), 'Command')) {
$class = (string) $node->getAttribute('class');
if (! Strings::endsWith($class, 'Command')) {
return false;
}
@ -103,7 +105,7 @@ final class CommandToConstructorInjectionRector extends AbstractRector
*/
public function refactor(Node $node): ?Node
{
$this->replaceParentContainerAwareCommandWithCommand();
$this->replaceParentContainerAwareCommandWithCommand($node);
$serviceType = $this->serviceFromKernelResolver->resolveServiceClassFromArgument(
$node->args[0],
@ -116,13 +118,18 @@ final class CommandToConstructorInjectionRector extends AbstractRector
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
$this->classPropertyCollector->addPropertyForClass($this->getClassName(), $serviceType, $propertyName);
$this->classPropertyCollector->addPropertyForClass(
(string) $node->getAttribute('class'),
$serviceType,
$propertyName
);
return $this->nodeFactory->createLocalPropertyFetch($propertyName);
}
private function replaceParentContainerAwareCommandWithCommand(): void
private function replaceParentContainerAwareCommandWithCommand(Node $node): void
{
$this->classNode->extends = new FullyQualified('Symfony\Component\Console\Command\Command');
$classNode = $node->getAttribute('class_node');
$classNode->extends = new FullyQualified('Symfony\Component\Console\Command\Command');
}
}

View File

@ -85,7 +85,11 @@ final class GetterToPropertyRector extends AbstractRector
$propertyName = $this->nameResolver->resolvePropertyNameFromType($serviceType);
$this->classPropertyCollector->addPropertyForClass($this->getClassName(), $serviceType, $propertyName);
$this->classPropertyCollector->addPropertyForClass(
(string) $methodCallNode->getAttribute('class'),
$serviceType,
$propertyName
);
return $this->nodeFactory->createLocalPropertyFetch($propertyName);
}

View File

@ -1,3 +1,6 @@
imports:
- { resource: '../../packages/TriggerExtractor/src/config/services.yml' }
parameters:
# todo
kernel_class: # for name based service refactoring
@ -7,7 +10,6 @@ parameters:
services:
_defaults:
autowire: true
autoconfigure: true
# PSR-4 autodiscovery
Rector\:
@ -21,7 +23,8 @@ services:
calls:
- ['addNodeVisitor', ['@PhpParser\NodeVisitor\NameResolver']]
- ['addNodeVisitor', ['@Rector\NodeVisitor\NodeConnector']]
- ['addNodeVisitor', ['@Rector\NodeTypeResolver\NodeVisitor\ClassLikeTypeResolver']]
- ['addNodeVisitor', ['@Rector\NodeTypeResolver\NodeVisitor\TypeResolver']]
- ['addNodeVisitor', ['@Rector\NodeTypeResolver\NodeVisitor\ClassResolver']]
# 3rd party services
Symfony\Component\Console\Application:

View File

@ -0,0 +1,15 @@
<?php declare (strict_types=1);
class SomePresenter
{
public function createNetteForm()
{
$form = new \Nette\Application\UI\Form;
$form->addText('name')
->setRequired(false)
->addRule('...')
->addRule('...');
return $form;
}
}

View File

@ -0,0 +1,25 @@
<?php declare(strict_types=1);
namespace Rector\Tests\Rector\Contrib\Nette\FormSetRequiredRector;
use Rector\Rector\Contrib\Nette\FormSetRequiredRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
final class Test extends AbstractRectorTestCase
{
public function test(): void
{
$this->doTestFileMatchesExpectedContent(
__DIR__ . '/Wrong/wrong.php.inc',
__DIR__ . '/Correct/correct.php.inc'
);
}
/**
* @return string[]
*/
protected function getRectorClasses(): array
{
return [FormSetRequiredRector::class];
}
}

View File

@ -0,0 +1,15 @@
<?php declare (strict_types=1);
class SomePresenter
{
public function createNetteForm()
{
$form = new \Nette\Application\UI\Form;
$form->addText('name')
->addCondition($form::FILLED)
->addRule('...')
->addRule('...');
return $form;
}
}