[Restoration] Add CompleteMissingDependencyInNewRector

This commit is contained in:
TomasVotruba 2020-05-29 22:34:22 +02:00
parent ad7d8bda36
commit f534eabd24
6 changed files with 284 additions and 2 deletions

View File

@ -1,4 +1,4 @@
# All 494 Rectors Overview
# All 495 Rectors Overview
- [Projects](#projects)
- [General](#general)
@ -55,7 +55,7 @@
- [Refactoring](#refactoring) (2)
- [RemovingStatic](#removingstatic) (4)
- [Renaming](#renaming) (10)
- [Restoration](#restoration) (3)
- [Restoration](#restoration) (4)
- [SOLID](#solid) (12)
- [Sensio](#sensio) (1)
- [StrictCodeQuality](#strictcodequality) (1)
@ -9091,6 +9091,42 @@ services:
<br>
### `CompleteMissingDependencyInNewRector`
- class: [`Rector\Restoration\Rector\New_\CompleteMissingDependencyInNewRector`](/../master/rules/restoration/src/Rector/New_/CompleteMissingDependencyInNewRector.php)
- [test fixtures](/../master/rules/restoration/tests/Rector/New_/CompleteMissingDependencyInNewRector/Fixture)
Complete missing constructor dependency instance by type
```yaml
services:
Rector\Restoration\Rector\New_\CompleteMissingDependencyInNewRector:
$classToInstantiateByType:
RandomDependency: RandomDependency
```
```diff
final class SomeClass
{
public function run()
{
- $valueObject = new RandomValueObject();
+ $valueObject = new RandomValueObject(new RandomDependency());
}
}
class RandomValueObject
{
public function __construct(RandomDependency $randomDependency)
{
}
}
```
<br>
### `MissingClassConstantReferenceToStringRector`
- class: [`Rector\Restoration\Rector\ClassConstFetch\MissingClassConstantReferenceToStringRector`](/../master/rules/restoration/src/Rector/ClassConstFetch/MissingClassConstantReferenceToStringRector.php)

View File

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Rector\New_;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name\FullyQualified;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use ReflectionClass;
use ReflectionMethod;
use ReflectionParameter;
/**
* @see \Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\CompleteMissingDependencyInNewRectorTest
*/
final class CompleteMissingDependencyInNewRector extends AbstractRector
{
/**
* @var string[]
*/
private $classToInstantiateByType = [];
/**
* @param string[] $classToInstantiateByType
*/
public function __construct(array $classToInstantiateByType = [])
{
$this->classToInstantiateByType = $classToInstantiateByType;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Complete missing constructor dependency instance by type', [
new ConfiguredCodeSample(
<<<'PHP'
final class SomeClass
{
public function run()
{
$valueObject = new RandomValueObject();
}
}
class RandomValueObject
{
public function __construct(RandomDependency $randomDependency)
{
}
}
PHP
,
<<<'PHP'
final class SomeClass
{
public function run()
{
$valueObject = new RandomValueObject(new RandomDependency());
}
}
class RandomValueObject
{
public function __construct(RandomDependency $randomDependency)
{
}
}
PHP
, [
'$classToInstantiateByType' => [
'RandomDependency' => 'RandomDependency',
],
]
),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [New_::class];
}
/**
* @param New_ $node
*/
public function refactor(Node $node): ?Node
{
if ($this->shouldSkipNew($node)) {
return null;
}
/** @var ReflectionMethod $constructorMethodReflection */
$constructorMethodReflection = $this->getNewNodeClassConstructorMethodReflection($node);
foreach ($constructorMethodReflection->getParameters() as $position => $parameterReflection) {
// argument is already set
if (isset($node->args[$position])) {
continue;
}
$classToInstantiate = $this->resolveClassToInstantiateByParameterReflection($parameterReflection);
if ($classToInstantiate === null) {
continue;
}
$new = new New_(new FullyQualified($classToInstantiate));
$node->args[$position] = new Arg($new);
}
return $node;
}
private function shouldSkipNew(New_ $new): bool
{
$constructorMethodReflection = $this->getNewNodeClassConstructorMethodReflection($new);
if ($constructorMethodReflection === null) {
return true;
}
return $constructorMethodReflection->getNumberOfRequiredParameters() <= count($new->args);
}
private function getNewNodeClassConstructorMethodReflection(New_ $new): ?ReflectionMethod
{
$className = $this->getName($new->class);
if ($className === null) {
return null;
}
if (! class_exists($className)) {
return null;
}
$reflectionClass = new ReflectionClass($className);
return $reflectionClass->getConstructor();
}
private function resolveClassToInstantiateByParameterReflection(ReflectionParameter $reflectionParameter): ?string
{
$parameterType = $reflectionParameter->getType();
if ($parameterType === null) {
return null;
}
$requiredType = (string) $parameterType;
return $this->classToInstantiateByType[$requiredType] ?? null;
}
}

View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Restoration\Rector\New_\CompleteMissingDependencyInNewRector;
use Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source\RandomDependency;
final class CompleteMissingDependencyInNewRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(string $file): void
{
$this->doTestFile($file);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorsWithConfiguration(): array
{
return [
CompleteMissingDependencyInNewRector::class => [
'$classToInstantiateByType' => [
RandomDependency::class => RandomDependency::class,
],
],
];
}
}

View File

@ -0,0 +1,31 @@
<?php
namespace Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Fixture;
use Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source\RandomValueObject;
final class SomeClass
{
public function run()
{
$valueObject = new RandomValueObject();
}
}
?>
-----
<?php
namespace Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Fixture;
use Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source\RandomValueObject;
final class SomeClass
{
public function run()
{
$valueObject = new RandomValueObject(new \Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source\RandomDependency());
}
}
?>

View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source;
final class RandomDependency
{
}

View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Tests\Rector\New_\CompleteMissingDependencyInNewRector\Source;
final class RandomValueObject
{
public function __construct(RandomDependency $randomDependency)
{
}
}