[Injection] Add StaticCallToAnotherServiceConstructorInjectionRector

This commit is contained in:
TomasVotruba 2020-07-24 13:14:32 +02:00
parent 407e0c4d33
commit 9c08060925
6 changed files with 281 additions and 3 deletions

View File

@ -145,7 +145,8 @@
"Rector\\NetteCodeQuality\\": "rules/nette-code-quality/src",
"Rector\\Decomplex\\": "rules/decomplex/src",
"Rector\\Downgrade\\": "rules/downgrade/src",
"Rector\\SymfonyPhpConfig\\": "rules/symfony-php-config/src"
"Rector\\SymfonyPhpConfig\\": "rules/symfony-php-config/src",
"Rector\\Injection\\": "rules/injection/src"
}
},
"autoload-dev": {
@ -225,7 +226,8 @@
"Rector\\NetteCodeQuality\\Tests\\": "rules/nette-code-quality/tests",
"Rector\\Decomplex\\Tests\\": "rules/decomplex/tests",
"Rector\\Downgrade\\Tests\\": "rules/downgrade/tests",
"Rector\\SymfonyPhpConfig\\Tests\\": "rules/symfony-php-config/tests"
"Rector\\SymfonyPhpConfig\\Tests\\": "rules/symfony-php-config/tests",
"Rector\\Injection\\Tests\\": "rules/injection/tests"
},
"classmap": [
"rules/cakephp/tests/Rector/Name/ImplicitShortClassNameUseStatementRector/Source",

View File

@ -39,7 +39,7 @@ final class ConfigurationFactory
$extraFileContent = isset($rectorRecipe['extra_file_content']) ? $this->normalizeCode(
$rectorRecipe['extra_file_content']
) : null;
$set = $this->setResolver->resolveSetByName($rectorRecipe['set']);
$set = $rectorRecipe['set'] ? $this->setResolver->resolveSetByName($rectorRecipe['set']) : null;
return new Configuration(
$rectorRecipe['package'],

View File

@ -0,0 +1,126 @@
<?php
declare(strict_types=1);
namespace Rector\Injection\Rector\StaticCall;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\Injection\ValueObject\StaticCallToMethodCall;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
/**
* @see \Rector\Injection\Tests\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector\StaticCallToAnotherServiceConstructorInjectionRectorTest
*/
final class StaticCallToAnotherServiceConstructorInjectionRector extends AbstractRector
{
/**
* @var StaticCallToMethodCall[]
*/
private $staticCallsToMethodCalls;
/**
* @var PropertyNaming
*/
private $propertyNaming;
/**
* @param StaticCallToMethodCall[] $staticCallsToMethodCalls
*/
public function __construct(array $staticCallsToMethodCalls, PropertyNaming $propertyNaming)
{
$this->staticCallsToMethodCalls = $staticCallsToMethodCalls;
$this->propertyNaming = $propertyNaming;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Change static call to service method via constructor injection', [
new ConfiguredCodeSample(
<<<'PHP'
use Nette\Utils\FileSystem;
class SomeClass
{
public function run()
{
return FileSystem::write('file', 'content');
}
}
PHP
,
<<<'PHP'
use Symplify\SmartFileSystem\SmartFileSystem;
class SomeClass
{
/**
* @var SmartFileSystem
*/
private $smartFileSystem;
public function __construct(SmartFileSystem $smartFileSystem)
{
$this->smartFileSystem = $smartFileSystem;
}
public function run()
{
return $this->smartFileSystem->dumpFile('file', 'content');
}
}
PHP
, [
'$staticCallsToMethodCalls' => [
new StaticCallToMethodCall(
'Nette\Utils\FileSystem',
'write',
'Symplify\SmartFileSystem\SmartFileSystem',
'dumpFile'
),
],
]),
]);
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [StaticCall::class];
}
/**
* @param StaticCall $node
*/
public function refactor(Node $node): ?Node
{
$class = $node->getAttribute(AttributeKey::CLASS_NODE);
if (! $class instanceof Class_) {
return null;
}
foreach ($this->staticCallsToMethodCalls as $staticCallsToMethodCall) {
if (! $staticCallsToMethodCall->matchStaticCall($node)) {
continue;
}
$serviceObjectType = new FullyQualifiedObjectType($staticCallsToMethodCall->getClassType());
$propertyName = $this->propertyNaming->fqnToVariableName($serviceObjectType);
$this->addPropertyToClass($class, $serviceObjectType, $propertyName);
$propertyFetchNode = $this->createPropertyFetch('this', $propertyName);
return new MethodCall($propertyFetchNode, $staticCallsToMethodCall->getMethodName(), $node->args);
}
return $node;
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Rector\Injection\ValueObject;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
final class StaticCallToMethodCall
{
/**
* @var string
*/
private $staticClass;
/**
* @var string
*/
private $staticMethod;
/**
* @var string
*/
private $classType;
/**
* @var string
*/
private $methodName;
public function __construct(string $staticClass, string $staticMethod, string $classType, string $methodName)
{
$this->staticClass = $staticClass;
$this->staticMethod = $staticMethod;
$this->classType = $classType;
$this->methodName = $methodName;
}
public function getClassType(): string
{
return $this->classType;
}
public function getMethodName(): string
{
return $this->methodName;
}
public function matchStaticCall(StaticCall $staticCall): bool
{
if (! $staticCall->class instanceof Name) {
return false;
}
$staticCallClassName = $staticCall->class->toString();
if ($staticCallClassName !== $this->staticClass) {
return false;
}
if (! $staticCall->name instanceof Identifier) {
return false;
}
$staticCallMethodName = $staticCall->name->toString();
return $staticCallMethodName === $this->staticMethod;
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Rector\Injection\Tests\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector\Fixture;
use Nette\Utils\FileSystem;
class SomeClass
{
public function run()
{
return FileSystem::write('file', 'content');
}
}
?>
-----
<?php
namespace Rector\Injection\Tests\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector\Fixture;
use Nette\Utils\FileSystem;
class SomeClass
{
private \Symplify\SmartFileSystem\SmartFileSystem $smartFileSystem;
public function __construct(\Symplify\SmartFileSystem\SmartFileSystem $smartFileSystem)
{
$this->smartFileSystem = $smartFileSystem;
}
public function run()
{
return $this->smartFileSystem->dumpFile('file', 'content');
}
}
?>

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Rector\Injection\Tests\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector;
use Iterator;
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
use Rector\Injection\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector;
use Rector\Injection\ValueObject\StaticCallToMethodCall;
use Symplify\SmartFileSystem\SmartFileInfo;
final class StaticCallToAnotherServiceConstructorInjectionRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorsWithConfiguration(): array
{
$configuration = [
new StaticCallToMethodCall(
'Nette\Utils\FileSystem',
'write',
'Symplify\SmartFileSystem\SmartFileSystem',
'dumpFile'
),
];
return [
StaticCallToAnotherServiceConstructorInjectionRector::class => [
'$staticCallsToMethodCalls' => $configuration,
],
];
}
}