re-use param in constructor (#3913)

This commit is contained in:
Tomas Votruba 2020-08-05 15:37:40 +02:00 committed by GitHub
parent 93905cb64c
commit 5e0dfbd77a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 113 additions and 13 deletions

View File

@ -21,7 +21,7 @@
"nikic/php-parser": "^4.7",
"ondram/ci-detector": "^3.4",
"phpstan/phpdoc-parser": "^0.4.8",
"phpstan/phpstan": "^0.12.35",
"phpstan/phpstan": "^0.12.36",
"phpstan/phpstan-phpunit": "^0.12.10",
"psr/simple-cache": "^1.0",
"sebastian/diff": "^3.0|^4.0",

View File

@ -8,7 +8,10 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
@ -18,6 +21,9 @@ use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use Rector\Core\ValueObject\MethodName;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStanStaticTypeMapper\Utils\TypeUnwrapper;
@ -33,16 +39,33 @@ final class TypeProvidingExprFromClassResolver
*/
private $reflectionProvider;
public function __construct(TypeUnwrapper $typeUnwrapper, ReflectionProvider $reflectionProvider)
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var PropertyNaming
*/
private $propertyNaming;
public function __construct(
TypeUnwrapper $typeUnwrapper,
ReflectionProvider $reflectionProvider,
NodeNameResolver $nodeNameResolver,
PropertyNaming $propertyNaming
) {
$this->typeUnwrapper = $typeUnwrapper;
$this->reflectionProvider = $reflectionProvider;
$this->nodeNameResolver = $nodeNameResolver;
$this->propertyNaming = $propertyNaming;
}
/**
* @return MethodCall|PropertyFetch|null
* @param ClassMethod|Function_ $functionLike
* @return MethodCall|PropertyFetch|Variable|null
*/
public function resolveTypeProvidingExprFromClass(Class_ $class, string $type): ?Expr
public function resolveTypeProvidingExprFromClass(Class_ $class, FunctionLike $functionLike, string $type): ?Expr
{
$className = $class->getAttribute(AttributeKey::CLASS_NAME);
if ($className === null) {
@ -62,7 +85,13 @@ final class TypeProvidingExprFromClassResolver
return null;
}
return $this->resolvePropertyFetchProvidingType($classReflection, $scope, $type);
$propertyFetch = $this->resolvePropertyFetchProvidingType($classReflection, $scope, $type);
if ($propertyFetch !== null) {
return $propertyFetch;
}
// C. param in constructor?
return $this->resolveConstructorParamProvidingType($functionLike, $type);
}
private function resolveMethodCallProvidingType(ClassReflection $classReflection, string $type): ?MethodCall
@ -118,4 +147,18 @@ final class TypeProvidingExprFromClassResolver
return $readableType->getClassName() === $type;
}
private function resolveConstructorParamProvidingType(FunctionLike $functionLike, string $type): ?Variable
{
if (! $functionLike instanceof ClassMethod) {
return null;
}
if (! $this->nodeNameResolver->isName($functionLike, MethodName::CONSTRUCT)) {
return null;
}
$variableName = $this->propertyNaming->fqnToVariableName($type);
return new Variable($variableName);
}
}

View File

@ -8,7 +8,10 @@ use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Generic\NodeTypeAnalyzer\TypeProvidingExprFromClassResolver;
@ -39,12 +42,21 @@ abstract class AbstractToMethodCallRector extends AbstractRector implements Conf
}
/**
* @return MethodCall|PropertyFetch
* @param ClassMethod|Function_ $functionLike
* @return MethodCall|PropertyFetch|Variable
*/
protected function matchTypeProvidingExpr(Class_ $class, string $type): Expr
protected function matchTypeProvidingExpr(Class_ $class, FunctionLike $functionLike, string $type): Expr
{
$expr = $this->typeProvidingExprFromClassResolver->resolveTypeProvidingExprFromClass($class, $type);
$expr = $this->typeProvidingExprFromClassResolver->resolveTypeProvidingExprFromClass(
$class,
$functionLike,
$type
);
if ($expr !== null) {
if ($expr instanceof Variable) {
$this->addClassMethodParamForVariable($expr, $type, $functionLike);
}
return $expr;
}
@ -66,4 +78,18 @@ abstract class AbstractToMethodCallRector extends AbstractRector implements Conf
return new PropertyFetch($thisVariable, $propertyName);
}
/**
* @param ClassMethod|Function_ $functionLike
*/
private function addClassMethodParamForVariable(Variable $variable, string $type, FunctionLike $functionLike): void
{
/** @var string $variableName */
$variableName = $this->getName($variable);
// add variable to __construct as dependency
$param = $this->nodeFactory->createParamFromNameAndType($variableName, new FullyQualifiedObjectType($type));
$functionLike->params[] = $param;
}
}

View File

@ -98,16 +98,16 @@ CODE_SAMPLE
return null;
}
foreach ($this->functionToClassToMethod as $function => $classMethod) {
foreach ($this->functionToClassToMethod as $function => $classMethodName) {
if (! $this->isName($node->name, $function)) {
continue;
}
/** @var string $type */
/** @var string $method */
[$type, $method] = $classMethod;
[$type, $method] = $classMethodName;
$expr = $this->matchTypeProvidingExpr($classLike, $type);
$expr = $this->matchTypeProvidingExpr($classLike, $classMethod, $type);
return $this->createMethodCall($expr, $method, $node->args);
}

View File

@ -116,7 +116,7 @@ PHP
return $this->refactorToInstanceCall($node, $staticCallToMethodCall);
}
$expr = $this->matchTypeProvidingExpr($classLike, $staticCallToMethodCall->getClassType());
$expr = $this->matchTypeProvidingExpr($classLike, $classMethod, $staticCallToMethodCall->getClassType());
return new MethodCall($expr, $staticCallToMethodCall->getMethodName(), $node->args);
}

View File

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