-
-
Notifications
You must be signed in to change notification settings - Fork 336
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Dominik Peters
committed
Aug 21, 2021
1 parent
29a6d94
commit 2a6cfb3
Showing
6 changed files
with
410 additions
and
99 deletions.
There are no files selected for viewing
73 changes: 73 additions & 0 deletions
73
...Quality/Rector/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/fixture_full.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector\Fixture; | ||
|
||
final class FixtureFull | ||
{ | ||
private \DateTime $date; | ||
|
||
private ?\DateTime $nullableDate; | ||
|
||
public function __construct(\DateTime $date, ?\DateTime $nullableDate) | ||
{ | ||
$this->date = $date; | ||
$this->nullableDate = $nullableDate; | ||
} | ||
|
||
public function getDate(): \DateTime | ||
{ | ||
return $this->date; | ||
} | ||
|
||
public function getNullableDate(): ?\DateTime | ||
{ | ||
return $this->nullableDate; | ||
} | ||
} | ||
|
||
?> | ||
----- | ||
<?php | ||
|
||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector\Fixture; | ||
|
||
final class FixtureFull | ||
{ | ||
/** | ||
* @var \DateTime|\DateTimeImmutable | ||
*/ | ||
private \DateTimeInterface $date; | ||
|
||
/** | ||
* @var \DateTime|\DateTimeImmutable|null | ||
*/ | ||
private ?\DateTimeInterface $nullableDate; | ||
|
||
/** | ||
* @param \DateTime|\DateTimeImmutable $date | ||
* @param \DateTime|\DateTimeImmutable|null $nullableDate | ||
*/ | ||
public function __construct(\DateTimeInterface $date, ?\DateTimeInterface $nullableDate) | ||
{ | ||
$this->date = $date; | ||
$this->nullableDate = $nullableDate; | ||
} | ||
|
||
/** | ||
* @return \DateTime|\DateTimeImmutable | ||
*/ | ||
public function getDate(): \DateTimeInterface | ||
{ | ||
return $this->date; | ||
} | ||
|
||
/** | ||
* @return \DateTime|\DateTimeImmutable|null | ||
*/ | ||
public function getNullableDate(): ?\DateTimeInterface | ||
{ | ||
return $this->nullableDate; | ||
} | ||
} | ||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
...tor/ClassMethod/DateTimeToDateTimeInterfaceRector/Fixture/skip_child_class_return.php.inc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
namespace Rector\Tests\CodeQuality\Rector\ClassMethod\DateTimeToDateTimeInterfaceRector\Fixture; | ||
|
||
use DateTime as PhpDateTime; | ||
|
||
class ReturnDateTime extends PhpDateTime | ||
{ | ||
public function getDateTimeCustomFormat(): ?string | ||
{ | ||
return $this->format('Y-m-d H:i:s'); | ||
} | ||
} | ||
|
||
class SkipChildClassReturn | ||
{ | ||
public static function bar(): ?ReturnDateTime | ||
{ | ||
// ... | ||
} | ||
|
||
public static function baz(): ReturnDateTime | ||
{ | ||
// ... | ||
} | ||
} |
166 changes: 166 additions & 0 deletions
166
rules/CodeQuality/NodeManipulator/ClassMethodParameterTypeManipulator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
|
||
namespace Rector\CodeQuality\NodeManipulator; | ||
|
||
use PhpParser\Node; | ||
use PhpParser\Node\Arg; | ||
use PhpParser\Node\Expr\Assign; | ||
use PhpParser\Node\Expr\MethodCall; | ||
use PhpParser\Node\Expr\Variable; | ||
use PhpParser\Node\Identifier; | ||
use PhpParser\Node\Name; | ||
use PhpParser\Node\NullableType; | ||
use PhpParser\Node\Param; | ||
use PhpParser\Node\Stmt\ClassMethod; | ||
use PhpParser\Node\Stmt\Expression; | ||
use PHPStan\Type\NullType; | ||
use PHPStan\Type\ObjectType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\UnionType; | ||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; | ||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; | ||
use Rector\Core\Exception\ShouldNotHappenException; | ||
use Rector\Core\NodeAnalyzer\ParamAnalyzer; | ||
use Rector\NodeNameResolver\NodeNameResolver; | ||
use Rector\NodeTypeResolver\Node\AttributeKey; | ||
use Rector\NodeTypeResolver\NodeTypeResolver; | ||
use Symplify\Astral\NodeTraverser\SimpleCallableNodeTraverser; | ||
|
||
final class ClassMethodParameterTypeManipulator | ||
{ | ||
public function __construct( | ||
private PhpDocInfoFactory $phpDocInfoFactory, | ||
private PhpDocTypeChanger $phpDocTypeChanger, | ||
private NodeTypeResolver $nodeTypeResolver, | ||
private ParamAnalyzer $paramAnalyzer, | ||
private NodeNameResolver $nodeNameResolver, | ||
private SimpleCallableNodeTraverser $simpleCallableNodeTraverser | ||
) { | ||
} | ||
|
||
/** | ||
* @param string[] $methodsReturningClassInstance | ||
*/ | ||
public function refactorFunctionParameters( | ||
ClassMethod $node, | ||
ObjectType $toReplaceType, | ||
Identifier|Name|NullableType $replaceIntoType, | ||
Type $phpDocType, | ||
array $methodsReturningClassInstance | ||
): void { | ||
foreach ($node->getParams() as $param) { | ||
if (! $this->nodeTypeResolver->isObjectType($param, $toReplaceType)) { | ||
continue; | ||
} | ||
|
||
$paramType = $this->nodeTypeResolver->resolve($param); | ||
if (! $paramType->isSuperTypeOf($toReplaceType)->yes()) { | ||
continue; | ||
} | ||
|
||
$this->refactorParamTypeHint($param, $replaceIntoType); | ||
$this->refactorParamDocBlock($param, $node, $phpDocType); | ||
$this->refactorMethodCalls($param, $node, $methodsReturningClassInstance); | ||
} | ||
} | ||
|
||
|
||
private function refactorParamTypeHint(Param $param, Identifier|Name|NullableType $replaceIntoType): void | ||
{ | ||
if ($this->paramAnalyzer->isNullable($param) && !$replaceIntoType instanceof NullableType) { | ||
$replaceIntoType = new NullableType($replaceIntoType); | ||
} | ||
|
||
$param->type = $replaceIntoType; | ||
} | ||
|
||
private function refactorParamDocBlock(Param $param, ClassMethod $classMethod, Type $phpDocType): void | ||
{ | ||
$paramName = $this->nodeNameResolver->getName($param->var); | ||
if ($paramName === null) { | ||
throw new ShouldNotHappenException(); | ||
} | ||
|
||
if ($this->paramAnalyzer->isNullable($param)) { | ||
if ($phpDocType instanceof UnionType) { | ||
// Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types | ||
$phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]); | ||
} else { | ||
$phpDocType = new UnionType([$phpDocType, new NullType()]); | ||
} | ||
} | ||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); | ||
$this->phpDocTypeChanger->changeParamType($phpDocInfo, $phpDocType, $param, $paramName); | ||
} | ||
|
||
/** | ||
* @param string[] $methodsReturningClassInstance | ||
*/ | ||
private function refactorMethodCalls(Param $param, ClassMethod $classMethod, array $methodsReturningClassInstance): void | ||
{ | ||
if ($classMethod->stmts === null) { | ||
return; | ||
} | ||
|
||
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($classMethod->stmts, function (Node $node) use ($param, $methodsReturningClassInstance): void { | ||
if (! ($node instanceof MethodCall)) { | ||
return; | ||
} | ||
|
||
$this->refactorMethodCall($param, $node, $methodsReturningClassInstance); | ||
}); | ||
} | ||
|
||
/** | ||
* @param string[] $methodsReturningClassInstance | ||
*/ | ||
private function refactorMethodCall(Param $param, MethodCall $methodCall, array $methodsReturningClassInstance): void | ||
{ | ||
$paramName = $this->nodeNameResolver->getName($param->var); | ||
if ($paramName === null) { | ||
return; | ||
} | ||
if ($this->shouldSkipMethodCallRefactor($paramName, $methodCall, $methodsReturningClassInstance)) { | ||
return; | ||
} | ||
|
||
$assign = new Assign(new Variable($paramName), $methodCall); | ||
|
||
/** @var Node $parent */ | ||
$parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE); | ||
if ($parent instanceof Arg) { | ||
$parent->value = $assign; | ||
return; | ||
} | ||
|
||
if (! $parent instanceof Expression) { | ||
return; | ||
} | ||
|
||
$parent->expr = $assign; | ||
} | ||
|
||
/** | ||
* @param string[] $methodsReturningClassInstance | ||
*/ | ||
private function shouldSkipMethodCallRefactor(string $paramName, MethodCall $methodCall, array $methodsReturningClassInstance): bool | ||
{ | ||
if (! $this->nodeNameResolver->isName($methodCall->var, $paramName)) { | ||
return true; | ||
} | ||
|
||
if (! $this->nodeNameResolver->isNames($methodCall->name, $methodsReturningClassInstance)) { | ||
return true; | ||
} | ||
|
||
$parentNode = $methodCall->getAttribute(AttributeKey::PARENT_NODE); | ||
if (! $parentNode instanceof Node) { | ||
return true; | ||
} | ||
|
||
return $parentNode instanceof Assign; | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
rules/CodeQuality/NodeManipulator/ClassMethodReturnTypeManipulator.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
|
||
namespace Rector\CodeQuality\NodeManipulator; | ||
|
||
use PhpParser\Node\Identifier; | ||
use PhpParser\Node\Name; | ||
use PhpParser\Node\NullableType; | ||
use PhpParser\Node\Stmt\ClassMethod; | ||
use PHPStan\Type\NullType; | ||
use PHPStan\Type\ObjectType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\UnionType; | ||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; | ||
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger; | ||
use Rector\NodeTypeResolver\NodeTypeResolver; | ||
|
||
final class ClassMethodReturnTypeManipulator | ||
{ | ||
public function __construct( | ||
private PhpDocInfoFactory $phpDocInfoFactory, | ||
private PhpDocTypeChanger $phpDocTypeChanger, | ||
private NodeTypeResolver $nodeTypeResolver | ||
) { | ||
} | ||
|
||
public function refactorFunctionReturnType( | ||
ClassMethod $classMethod, | ||
ObjectType $toReplaceType, | ||
Identifier|Name|NullableType $replaceIntoType, | ||
Type $phpDocType | ||
): void { | ||
$returnType = $classMethod->returnType; | ||
if ($returnType === null) { | ||
return; | ||
} | ||
|
||
$isNullable = false; | ||
if ($returnType instanceof NullableType) { | ||
$isNullable = true; | ||
$returnType = $returnType->type; | ||
} | ||
if (! $this->nodeTypeResolver->isObjectType($returnType, $toReplaceType)) { | ||
return; | ||
} | ||
|
||
$paramType = $this->nodeTypeResolver->resolve($returnType); | ||
if (! $paramType->isSuperTypeOf($toReplaceType)->yes()) { | ||
return; | ||
} | ||
|
||
if ($isNullable) { | ||
if ($phpDocType instanceof UnionType) { | ||
// Adding a UnionType into a new UnionType throws an exception so we need to "unpack" the types | ||
$phpDocType = new UnionType([...$phpDocType->getTypes(), new NullType()]); | ||
} else { | ||
$phpDocType = new UnionType([$phpDocType, new NullType()]); | ||
} | ||
if (!$replaceIntoType instanceof NullableType) { | ||
$replaceIntoType = new NullableType($replaceIntoType); | ||
} | ||
} | ||
$classMethod->returnType = $replaceIntoType; | ||
|
||
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); | ||
$this->phpDocTypeChanger->changeReturnType($phpDocInfo, $phpDocType); | ||
} | ||
} |
Oops, something went wrong.