Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchAssignManipulator;
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchManipulator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface;
Expand All @@ -25,9 +26,17 @@ final class GetterNodeParamTypeInferer extends AbstractTypeInferer implements Pa
*/
private $propertyFetchManipulator;

public function __construct(PropertyFetchManipulator $propertyFetchManipulator)
{
/**
* @var PropertyFetchAssignManipulator
*/
private $propertyFetchAssignManipulator;

public function __construct(
PropertyFetchManipulator $propertyFetchManipulator,
PropertyFetchAssignManipulator $propertyFetchAssignManipulator
) {
$this->propertyFetchManipulator = $propertyFetchManipulator;
$this->propertyFetchAssignManipulator = $propertyFetchAssignManipulator;
}

public function inferParam(Param $param): Type
Expand All @@ -44,7 +53,10 @@ public function inferParam(Param $param): Type
/** @var string $paramName */
$paramName = $this->nodeNameResolver->getName($param);

$propertyNames = $this->propertyFetchManipulator->getPropertyNamesOfAssignOfVariable($classMethod, $paramName);
$propertyNames = $this->propertyFetchAssignManipulator->getPropertyNamesOfAssignOfVariable(
$classMethod,
$paramName
);
if ($propertyNames === []) {
return new MixedType();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use PHPStan\Type\NullType;
use PHPStan\Type\Type;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\Manipulator\PropertyFetchManipulator;
use Rector\Core\PhpParser\Node\Manipulator\ClassMethodPropertyFetchManipulator;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
Expand All @@ -30,13 +30,13 @@
final class ConstructorPropertyTypeInferer extends AbstractTypeInferer implements PropertyTypeInfererInterface
{
/**
* @var PropertyFetchManipulator
* @var ClassMethodPropertyFetchManipulator
*/
private $propertyFetchManipulator;
private $classMethodPropertyFetchManipulator;

public function __construct(PropertyFetchManipulator $propertyFetchManipulator)
public function __construct(ClassMethodPropertyFetchManipulator $classMethodPropertyFetchManipulator)
{
$this->propertyFetchManipulator = $propertyFetchManipulator;
$this->classMethodPropertyFetchManipulator = $classMethodPropertyFetchManipulator;
}

public function inferProperty(Property $property): Type
Expand All @@ -58,7 +58,7 @@ public function inferProperty(Property $property): Type
throw new ShouldNotHappenException();
}

$param = $this->propertyFetchManipulator->resolveParamForPropertyFetch($classMethod, $propertyName);
$param = $this->classMethodPropertyFetchManipulator->resolveParamForPropertyFetch($classMethod, $propertyName);
if ($param === null) {
return new MixedType();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

declare(strict_types=1);

namespace Rector\Core\PhpParser\Node\Manipulator;

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\NodeTraverser;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeNameResolver\NodeNameResolver;

final class ClassMethodPropertyFetchManipulator
{
/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;

/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

public function __construct(CallableNodeTraverser $callableNodeTraverser, NodeNameResolver $nodeNameResolver)
{
$this->callableNodeTraverser = $callableNodeTraverser;
$this->nodeNameResolver = $nodeNameResolver;
}

/**
* In case the property name is different to param name:
*
* E.g.:
* (SomeType $anotherValue)
* $this->value = $anotherValue;
* ↓
* (SomeType $anotherValue)
*/
public function resolveParamForPropertyFetch(ClassMethod $classMethod, string $propertyName): ?Param
{
$assignedParamName = null;

$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
$propertyName,
&$assignedParamName
): ?int {
if (! $node instanceof Assign) {
return null;
}

if (! $this->nodeNameResolver->isName($node->var, $propertyName)) {
return null;
}

$assignedParamName = $this->nodeNameResolver->getName($node->expr);

return NodeTraverser::STOP_TRAVERSAL;
});

/** @var string|null $assignedParamName */
if ($assignedParamName === null) {
return null;
}

/** @var Param $param */
foreach ((array) $classMethod->params as $param) {
if (! $this->nodeNameResolver->isName($param, $assignedParamName)) {
continue;
}

return $param;
}

return null;
}
}
83 changes: 83 additions & 0 deletions src/PhpParser/Node/Manipulator/PropertyFetchAssignManipulator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

declare(strict_types=1);

namespace Rector\Core\PhpParser\Node\Manipulator;

use PhpParser\Node;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeNameResolver\NodeNameResolver;

final class PropertyFetchAssignManipulator
{
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;

/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;

public function __construct(NodeNameResolver $nodeNameResolver, CallableNodeTraverser $callableNodeTraverser)
{
$this->nodeNameResolver = $nodeNameResolver;
$this->callableNodeTraverser = $callableNodeTraverser;
}

/**
* @return string[]
*/
public function getPropertyNamesOfAssignOfVariable(Node $node, string $paramName): array
{
$propertyNames = [];

$this->callableNodeTraverser->traverseNodesWithCallable($node, function (Node $node) use (
$paramName,
&$propertyNames
) {
if (! $this->isVariableAssignToThisPropertyFetch($node, $paramName)) {
return null;
}

/** @var Assign $node */
$propertyName = $this->nodeNameResolver->getName($node->expr);
if ($propertyName) {
$propertyNames[] = $propertyName;
}

return null;
});

return $propertyNames;
}

/**
* Matches:
* "$this->someValue = $<variableName>;"
*/
public function isVariableAssignToThisPropertyFetch(Node $node, string $variableName): bool
{
if (! $node instanceof Assign) {
return false;
}

if (! $node->expr instanceof Variable) {
return false;
}

if (! $this->nodeNameResolver->isName($node->expr, $variableName)) {
return false;
}

if (! $node->var instanceof PropertyFetch) {
return false;
}
// must be local property
return $this->nodeNameResolver->isName($node->var->var, 'this');
}
}
71 changes: 8 additions & 63 deletions src/PhpParser/Node/Manipulator/PropertyFetchManipulator.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,11 @@
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\NodeTraverser;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ErrorType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeNameResolver\NodeNameResolver;
Expand Down Expand Up @@ -86,13 +84,9 @@ public function isPropertyToSelf(PropertyFetch $propertyFetch): bool
return false;
}

public function isMagicOnType(Node $node, Type $type): bool
public function isMagicOnType(PropertyFetch $propertyFetch, Type $type): bool
{
if (! $node instanceof PropertyFetch) {
return false;
}

$varNodeType = $this->nodeTypeResolver->resolve($node);
$varNodeType = $this->nodeTypeResolver->resolve($propertyFetch);

if ($varNodeType instanceof ErrorType) {
return true;
Expand All @@ -106,12 +100,12 @@ public function isMagicOnType(Node $node, Type $type): bool
return false;
}

$nodeName = $this->nodeNameResolver->getName($node);
$nodeName = $this->nodeNameResolver->getName($propertyFetch);
if ($nodeName === null) {
return false;
}

return ! $this->hasPublicProperty($node, $nodeName);
return ! $this->hasPublicProperty($propertyFetch, $nodeName);
}

/**
Expand Down Expand Up @@ -162,6 +156,7 @@ public function isVariableAssignToThisPropertyFetch(Node $node, string $variable
if (! $node->var instanceof PropertyFetch) {
return false;
}

// must be local property
return $this->nodeNameResolver->isName($node->var->var, 'this');
}
Expand All @@ -188,53 +183,6 @@ public function isLocalProperty(Node $node): bool
return $this->nodeNameResolver->isName($node->var, 'this');
}

/**
* In case the property name is different to param name:
*
* E.g.:
* (SomeType $anotherValue)
* $this->value = $anotherValue;
* ↓
* (SomeType $anotherValue)
*/
public function resolveParamForPropertyFetch(ClassMethod $classMethod, string $propertyName): ?Param
{
$assignedParamName = null;

$this->callableNodeTraverser->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
$propertyName,
&$assignedParamName
): ?int {
if (! $node instanceof Assign) {
return null;
}

if (! $this->nodeNameResolver->isName($node->var, $propertyName)) {
return null;
}

$assignedParamName = $this->nodeNameResolver->getName($node->expr);

return NodeTraverser::STOP_TRAVERSAL;
});

/** @var string|null $assignedParamName */
if ($assignedParamName === null) {
return null;
}

/** @var Param $param */
foreach ((array) $classMethod->params as $param) {
if (! $this->nodeNameResolver->isName($param, $assignedParamName)) {
continue;
}

return $param;
}

return null;
}

/**
* @return PropertyFetch|StaticPropertyFetch|null
*/
Expand Down Expand Up @@ -290,14 +238,11 @@ private function hasPublicProperty(PropertyFetch $propertyFetch, string $propert
}

$propertyFetchType = $nodeScope->getType($propertyFetch->var);
if ($propertyFetchType instanceof ObjectType) {
$propertyFetchType = $propertyFetchType->getClassName();
}

if (! is_string($propertyFetchType)) {
if (! $propertyFetchType instanceof TypeWithClassName) {
return false;
}

$propertyFetchType = $propertyFetchType->getClassName();
if (! $this->reflectionProvider->hasClass($propertyFetchType)) {
return false;
}
Expand Down