mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-01 08:50:50 +00:00
f3d9b9c31c
5493b6dc13
[DX] Re-use configure() in docs + remove prefixing of ValueObjectInliner (#1410)
221 lines
10 KiB
PHP
221 lines
10 KiB
PHP
<?php
|
|
|
|
declare (strict_types=1);
|
|
namespace Rector\Transform\Rector\FuncCall;
|
|
|
|
use PhpParser\Node;
|
|
use PhpParser\Node\Arg;
|
|
use PhpParser\Node\Expr\FuncCall;
|
|
use PhpParser\Node\Expr\MethodCall;
|
|
use PhpParser\Node\Expr\PropertyFetch;
|
|
use PhpParser\Node\Stmt\Class_;
|
|
use PhpParser\Node\Stmt\ClassMethod;
|
|
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
|
use Rector\Core\Exception\ShouldNotHappenException;
|
|
use Rector\Core\Rector\AbstractRector;
|
|
use Rector\Naming\Naming\PropertyNaming;
|
|
use Rector\Naming\ValueObject\ExpectedName;
|
|
use Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer;
|
|
use Rector\PostRector\Collector\PropertyToAddCollector;
|
|
use Rector\PostRector\ValueObject\PropertyMetadata;
|
|
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
|
use Rector\Transform\Contract\ValueObject\ArgumentFuncCallToMethodCallInterface;
|
|
use Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall;
|
|
use Rector\Transform\ValueObject\ArrayFuncCallToMethodCall;
|
|
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
|
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
|
use RectorPrefix20211206\Webmozart\Assert\Assert;
|
|
/**
|
|
* @see \Rector\Tests\Transform\Rector\FuncCall\ArgumentFuncCallToMethodCallRector\ArgumentFuncCallToMethodCallRectorTest
|
|
*/
|
|
final class ArgumentFuncCallToMethodCallRector extends \Rector\Core\Rector\AbstractRector implements \Rector\Core\Contract\Rector\ConfigurableRectorInterface
|
|
{
|
|
/**
|
|
* @var string
|
|
*/
|
|
public const FUNCTIONS_TO_METHOD_CALLS = 'functions_to_method_calls';
|
|
/**
|
|
* @var ArgumentFuncCallToMethodCallInterface[]
|
|
*/
|
|
private $argumentFuncCallToMethodCalls = [];
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer
|
|
*/
|
|
private $arrayTypeAnalyzer;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\Naming\Naming\PropertyNaming
|
|
*/
|
|
private $propertyNaming;
|
|
/**
|
|
* @readonly
|
|
* @var \Rector\PostRector\Collector\PropertyToAddCollector
|
|
*/
|
|
private $propertyToAddCollector;
|
|
public function __construct(\Rector\NodeTypeResolver\TypeAnalyzer\ArrayTypeAnalyzer $arrayTypeAnalyzer, \Rector\Naming\Naming\PropertyNaming $propertyNaming, \Rector\PostRector\Collector\PropertyToAddCollector $propertyToAddCollector)
|
|
{
|
|
$this->arrayTypeAnalyzer = $arrayTypeAnalyzer;
|
|
$this->propertyNaming = $propertyNaming;
|
|
$this->propertyToAddCollector = $propertyToAddCollector;
|
|
}
|
|
public function getRuleDefinition() : \Symplify\RuleDocGenerator\ValueObject\RuleDefinition
|
|
{
|
|
return new \Symplify\RuleDocGenerator\ValueObject\RuleDefinition('Move help facade-like function calls to constructor injection', [new \Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample(<<<'CODE_SAMPLE'
|
|
class SomeController
|
|
{
|
|
public function action()
|
|
{
|
|
$template = view('template.blade');
|
|
$viewFactory = view();
|
|
}
|
|
}
|
|
CODE_SAMPLE
|
|
, <<<'CODE_SAMPLE'
|
|
class SomeController
|
|
{
|
|
/**
|
|
* @var \Illuminate\Contracts\View\Factory
|
|
*/
|
|
private $viewFactory;
|
|
|
|
public function __construct(\Illuminate\Contracts\View\Factory $viewFactory)
|
|
{
|
|
$this->viewFactory = $viewFactory;
|
|
}
|
|
|
|
public function action()
|
|
{
|
|
$template = $this->viewFactory->make('template.blade');
|
|
$viewFactory = $this->viewFactory;
|
|
}
|
|
}
|
|
CODE_SAMPLE
|
|
, [new \Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall('view', 'Illuminate\\Contracts\\View\\Factory', 'make')])]);
|
|
}
|
|
/**
|
|
* @return array<class-string<Node>>
|
|
*/
|
|
public function getNodeTypes() : array
|
|
{
|
|
return [\PhpParser\Node\Expr\FuncCall::class];
|
|
}
|
|
/**
|
|
* @param FuncCall $node
|
|
*/
|
|
public function refactor(\PhpParser\Node $node) : ?\PhpParser\Node
|
|
{
|
|
if ($this->shouldSkipFuncCall($node)) {
|
|
return null;
|
|
}
|
|
/** @var Class_ $classLike */
|
|
$classLike = $this->betterNodeFinder->findParentType($node, \PhpParser\Node\Stmt\Class_::class);
|
|
foreach ($this->argumentFuncCallToMethodCalls as $argumentFuncCallToMethodCall) {
|
|
if (!$this->isName($node, $argumentFuncCallToMethodCall->getFunction())) {
|
|
continue;
|
|
}
|
|
if ($argumentFuncCallToMethodCall instanceof \Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall) {
|
|
return $this->refactorFuncCallToMethodCall($argumentFuncCallToMethodCall, $classLike, $node);
|
|
}
|
|
if ($argumentFuncCallToMethodCall instanceof \Rector\Transform\ValueObject\ArrayFuncCallToMethodCall) {
|
|
return $this->refactorArrayFunctionToMethodCall($argumentFuncCallToMethodCall, $node, $classLike);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* @param mixed[] $configuration
|
|
*/
|
|
public function configure(array $configuration) : void
|
|
{
|
|
$functionToMethodCalls = $configuration[self::FUNCTIONS_TO_METHOD_CALLS] ?? $configuration;
|
|
\RectorPrefix20211206\Webmozart\Assert\Assert::isArray($functionToMethodCalls);
|
|
\RectorPrefix20211206\Webmozart\Assert\Assert::allIsAOf($functionToMethodCalls, \Rector\Transform\Contract\ValueObject\ArgumentFuncCallToMethodCallInterface::class);
|
|
$this->argumentFuncCallToMethodCalls = $functionToMethodCalls;
|
|
}
|
|
private function shouldSkipFuncCall(\PhpParser\Node\Expr\FuncCall $funcCall) : bool
|
|
{
|
|
// we can inject only in injectable class method context
|
|
/** @var ClassMethod|null $classMethod */
|
|
$classMethod = $this->betterNodeFinder->findParentType($funcCall, \PhpParser\Node\Stmt\ClassMethod::class);
|
|
if (!$classMethod instanceof \PhpParser\Node\Stmt\ClassMethod) {
|
|
return \true;
|
|
}
|
|
return $classMethod->isStatic();
|
|
}
|
|
/**
|
|
* @return PropertyFetch|MethodCall
|
|
*/
|
|
private function refactorFuncCallToMethodCall(\Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall, \PhpParser\Node\Stmt\Class_ $class, \PhpParser\Node\Expr\FuncCall $funcCall) : ?\PhpParser\Node
|
|
{
|
|
$fullyQualifiedObjectType = new \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType($argumentFuncCallToMethodCall->getClass());
|
|
$expectedName = $this->propertyNaming->getExpectedNameFromType($fullyQualifiedObjectType);
|
|
if (!$expectedName instanceof \Rector\Naming\ValueObject\ExpectedName) {
|
|
throw new \Rector\Core\Exception\ShouldNotHappenException();
|
|
}
|
|
$propertyMetadata = new \Rector\PostRector\ValueObject\PropertyMetadata($expectedName->getName(), $fullyQualifiedObjectType, \PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE);
|
|
$this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata);
|
|
$propertyFetchNode = $this->nodeFactory->createPropertyFetch('this', $expectedName->getName());
|
|
if ($funcCall->args === []) {
|
|
return $this->refactorEmptyFuncCallArgs($argumentFuncCallToMethodCall, $propertyFetchNode);
|
|
}
|
|
if ($this->isFunctionToMethodCallWithArgs($funcCall, $argumentFuncCallToMethodCall)) {
|
|
$methodName = $argumentFuncCallToMethodCall->getMethodIfArgs();
|
|
if (!\is_string($methodName)) {
|
|
throw new \Rector\Core\Exception\ShouldNotHappenException();
|
|
}
|
|
return new \PhpParser\Node\Expr\MethodCall($propertyFetchNode, $methodName, $funcCall->args);
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* @return PropertyFetch|MethodCall|null
|
|
*/
|
|
private function refactorArrayFunctionToMethodCall(\Rector\Transform\ValueObject\ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall, \PhpParser\Node\Expr\FuncCall $funcCall, \PhpParser\Node\Stmt\Class_ $class) : ?\PhpParser\Node
|
|
{
|
|
$propertyName = $this->propertyNaming->fqnToVariableName($arrayFuncCallToMethodCall->getClass());
|
|
$propertyFetch = $this->nodeFactory->createPropertyFetch('this', $propertyName);
|
|
$fullyQualifiedObjectType = new \Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType($arrayFuncCallToMethodCall->getClass());
|
|
$propertyMetadata = new \Rector\PostRector\ValueObject\PropertyMetadata($propertyName, $fullyQualifiedObjectType, \PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE);
|
|
$this->propertyToAddCollector->addPropertyToClass($class, $propertyMetadata);
|
|
return $this->createMethodCallArrayFunctionToMethodCall($funcCall, $arrayFuncCallToMethodCall, $propertyFetch);
|
|
}
|
|
/**
|
|
* @return \PhpParser\Node\Expr\MethodCall|\PhpParser\Node\Expr\PropertyFetch
|
|
*/
|
|
private function refactorEmptyFuncCallArgs(\Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall, \PhpParser\Node\Expr\PropertyFetch $propertyFetch)
|
|
{
|
|
if ($argumentFuncCallToMethodCall->getMethodIfNoArgs() !== null) {
|
|
$methodName = $argumentFuncCallToMethodCall->getMethodIfNoArgs();
|
|
return new \PhpParser\Node\Expr\MethodCall($propertyFetch, $methodName);
|
|
}
|
|
return $propertyFetch;
|
|
}
|
|
private function isFunctionToMethodCallWithArgs(\PhpParser\Node\Expr\FuncCall $funcCall, \Rector\Transform\ValueObject\ArgumentFuncCallToMethodCall $argumentFuncCallToMethodCall) : bool
|
|
{
|
|
if ($argumentFuncCallToMethodCall->getMethodIfArgs() === null) {
|
|
return \false;
|
|
}
|
|
return \count($funcCall->args) >= 1;
|
|
}
|
|
/**
|
|
* @return PropertyFetch|MethodCall|null
|
|
*/
|
|
private function createMethodCallArrayFunctionToMethodCall(\PhpParser\Node\Expr\FuncCall $funcCall, \Rector\Transform\ValueObject\ArrayFuncCallToMethodCall $arrayFuncCallToMethodCall, \PhpParser\Node\Expr\PropertyFetch $propertyFetch) : ?\PhpParser\Node
|
|
{
|
|
if ($funcCall->args === []) {
|
|
return $propertyFetch;
|
|
}
|
|
if (!$funcCall->args[0] instanceof \PhpParser\Node\Arg) {
|
|
return null;
|
|
}
|
|
if ($this->arrayTypeAnalyzer->isArrayType($funcCall->args[0]->value)) {
|
|
return new \PhpParser\Node\Expr\MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getArrayMethod(), $funcCall->args);
|
|
}
|
|
if ($arrayFuncCallToMethodCall->getNonArrayMethod() === '') {
|
|
return null;
|
|
}
|
|
return new \PhpParser\Node\Expr\MethodCall($propertyFetch, $arrayFuncCallToMethodCall->getNonArrayMethod(), $funcCall->args);
|
|
}
|
|
}
|