Skip to content

Commit 5cc97f5

Browse files
committed
Updated Rector to commit 643814dc92ceacb159adbac79c1ab6fb6e5cc2ee
rectorphp/rector-src@643814d [TypeDeclaration] Add ChildDoctrineRepositoryClassTypeRector (#5695)
1 parent bd3297e commit 5cc97f5

File tree

5 files changed

+194
-2
lines changed

5 files changed

+194
-2
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<?php
2+
3+
declare (strict_types=1);
4+
namespace Rector\TypeDeclaration\Rector\Class_;
5+
6+
use PhpParser\Node;
7+
use PhpParser\Node\Expr\MethodCall;
8+
use PhpParser\Node\Identifier;
9+
use PhpParser\Node\Name;
10+
use PhpParser\Node\NullableType;
11+
use PhpParser\Node\Stmt\Class_;
12+
use PhpParser\Node\Stmt\ClassMethod;
13+
use PhpParser\NodeFinder;
14+
use PHPStan\PhpDocParser\Ast\PhpDoc\ExtendsTagValueNode;
15+
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
16+
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
17+
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
18+
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
19+
use PHPStan\Type\ObjectType;
20+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
21+
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
22+
use Rector\Comments\NodeDocBlock\DocBlockUpdater;
23+
use Rector\Rector\AbstractRector;
24+
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
25+
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
26+
/**
27+
* @see \Rector\Tests\TypeDeclaration\Rector\Class_\ChildDoctrineRepositoryClassTypeRector\ChildDoctrineRepositoryClassTypeRectorTest
28+
*/
29+
final class ChildDoctrineRepositoryClassTypeRector extends AbstractRector
30+
{
31+
/**
32+
* @readonly
33+
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
34+
*/
35+
private $phpDocInfoFactory;
36+
/**
37+
* @readonly
38+
* @var \PhpParser\NodeFinder
39+
*/
40+
private $nodeFinder;
41+
/**
42+
* @readonly
43+
* @var \Rector\Comments\NodeDocBlock\DocBlockUpdater
44+
*/
45+
private $docBlockUpdater;
46+
public function __construct(PhpDocInfoFactory $phpDocInfoFactory, NodeFinder $nodeFinder, DocBlockUpdater $docBlockUpdater)
47+
{
48+
$this->phpDocInfoFactory = $phpDocInfoFactory;
49+
$this->nodeFinder = $nodeFinder;
50+
$this->docBlockUpdater = $docBlockUpdater;
51+
}
52+
public function getRuleDefinition() : RuleDefinition
53+
{
54+
return new RuleDefinition('Add return type to classes that extend Doctrine\\ORM\\EntityRepository', [new CodeSample(<<<'CODE_SAMPLE'
55+
use Doctrine\ORM\EntityRepository;
56+
57+
/**
58+
* @extends EntityRepository<SomeType>
59+
*/
60+
final class SomeRepository extends EntityRepository
61+
{
62+
public function getActiveItem()
63+
{
64+
return $this->findOneBy([
65+
'something'
66+
]);
67+
}
68+
}
69+
CODE_SAMPLE
70+
, <<<'CODE_SAMPLE'
71+
use Doctrine\ORM\EntityRepository;
72+
73+
/**
74+
* @extends EntityRepository<SomeType>
75+
*/
76+
final class SomeRepository extends EntityRepository
77+
{
78+
public function getActiveItem(): ?SomeType
79+
{
80+
return $this->findOneBy([
81+
'something'
82+
]);
83+
}
84+
}
85+
CODE_SAMPLE
86+
)]);
87+
}
88+
/**
89+
* @return array<class-string<Node>>
90+
*/
91+
public function getNodeTypes() : array
92+
{
93+
return [Class_::class];
94+
}
95+
/**
96+
* @param Class_ $node
97+
*/
98+
public function refactor(Node $node) : ?Node
99+
{
100+
if (!$this->isObjectType($node, new ObjectType('Doctrine\\ORM\\EntityRepository'))) {
101+
return null;
102+
}
103+
$entityClassName = $this->resolveEntityClassnameFromPhpDoc($node);
104+
if ($entityClassName === null) {
105+
return null;
106+
}
107+
$hasChanged = \false;
108+
foreach ($node->getMethods() as $classMethod) {
109+
if ($this->shouldSkipClassMethod($classMethod)) {
110+
continue;
111+
}
112+
if ($this->containsMethodCallNamed($classMethod, 'findOneBy')) {
113+
$classMethod->returnType = $this->createNullableType($entityClassName);
114+
}
115+
if ($this->containsMethodCallNamed($classMethod, 'findBy')) {
116+
$classMethod->returnType = new Identifier('array');
117+
// add docblock with type
118+
$classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
119+
$arrayTypeNode = new ArrayTypeNode(new IdentifierTypeNode($entityClassName));
120+
$classMethodPhpDocInfo->addTagValueNode(new ReturnTagValueNode($arrayTypeNode, ''));
121+
$this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($classMethod);
122+
}
123+
$hasChanged = \true;
124+
// try to figure out the return type
125+
}
126+
if ($hasChanged) {
127+
return $node;
128+
}
129+
return null;
130+
}
131+
private function resolveEntityClassnameFromPhpDoc(Class_ $class) : ?string
132+
{
133+
$classPhpDocInfo = $this->phpDocInfoFactory->createFromNode($class);
134+
// we need a way to resolve entity type... 1st idea is from @extends docblock
135+
if (!$classPhpDocInfo instanceof PhpDocInfo) {
136+
return null;
137+
}
138+
$extendsTagValuePhpDocNodes = $classPhpDocInfo->getTagsByName('extends');
139+
if ($extendsTagValuePhpDocNodes === []) {
140+
return null;
141+
}
142+
$extendsTagValueNode = $extendsTagValuePhpDocNodes[0]->value;
143+
if (!$extendsTagValueNode instanceof ExtendsTagValueNode) {
144+
return null;
145+
}
146+
// we look for generic type class
147+
if (!$extendsTagValueNode->type instanceof GenericTypeNode) {
148+
return null;
149+
}
150+
$genericTypeNode = $extendsTagValueNode->type;
151+
if ($genericTypeNode->type->name !== 'EntityRepository') {
152+
return null;
153+
}
154+
$entityGenericType = $genericTypeNode->genericTypes[0];
155+
if (!$entityGenericType instanceof IdentifierTypeNode) {
156+
return null;
157+
}
158+
return $entityGenericType->name;
159+
}
160+
private function containsMethodCallNamed(ClassMethod $classMethod, string $desiredMethodName) : bool
161+
{
162+
return (bool) $this->nodeFinder->findFirst((array) $classMethod->stmts, static function (Node $node) use($desiredMethodName) : bool {
163+
if (!$node instanceof MethodCall) {
164+
return \false;
165+
}
166+
if (!$node->name instanceof Identifier) {
167+
return \false;
168+
}
169+
$currentMethodCallName = $node->name->toString();
170+
return $currentMethodCallName === $desiredMethodName;
171+
});
172+
}
173+
private function shouldSkipClassMethod(ClassMethod $classMethod) : bool
174+
{
175+
if (!$classMethod->isPublic()) {
176+
return \true;
177+
}
178+
if ($classMethod->isStatic()) {
179+
return \true;
180+
}
181+
return $classMethod->returnType instanceof Node;
182+
}
183+
private function createNullableType(string $entityClassName) : NullableType
184+
{
185+
$name = new Name($entityClassName);
186+
return new NullableType($name);
187+
}
188+
}

src/Application/VersionResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ final class VersionResolver
1919
* @api
2020
* @var string
2121
*/
22-
public const PACKAGE_VERSION = '84639e6aa9c5daa4958121e9aa19e36b5b6bb9b6';
22+
public const PACKAGE_VERSION = '643814dc92ceacb159adbac79c1ab6fb6e5cc2ee';
2323
/**
2424
* @api
2525
* @var string
2626
*/
27-
public const RELEASE_DATE = '2024-03-06 05:46:53';
27+
public const RELEASE_DATE = '2024-03-06 20:34:50';
2828
/**
2929
* @var int
3030
*/

src/Config/Level/TypeDeclarationLevel.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Rector\Contract\Rector\RectorInterface;
77
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
88
use Rector\TypeDeclaration\Rector\Class_\AddTestsVoidReturnTypeWhereNoReturnRector;
9+
use Rector\TypeDeclaration\Rector\Class_\ChildDoctrineRepositoryClassTypeRector;
910
use Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector;
1011
use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector;
1112
use Rector\TypeDeclaration\Rector\Class_\ReturnTypeFromStrictTernaryRector;
@@ -78,6 +79,7 @@ final class TypeDeclarationLevel
7879
TypedPropertyFromStrictSetUpRector::class,
7980
ReturnTypeFromStrictNativeCallRector::class,
8081
ReturnTypeFromStrictTypedCallRector::class,
82+
ChildDoctrineRepositoryClassTypeRector::class,
8183
// param
8284
AddMethodCallBasedStrictParamTypeRector::class,
8385
ParamTypeByParentCallTypeRector::class,

vendor/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,7 @@
23852385
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php',
23862386
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php',
23872387
'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php',
2388+
'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php',
23882389
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
23892390
'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php',
23902391
'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => $baseDir . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php',

vendor/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,7 @@ class ComposerStaticInit2d887a2f87c676eb32b3e04612865e54
26042604
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictArrayParamDimFetchRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictArrayParamDimFetchRector.php',
26052605
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\StrictStringParamConcatRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/StrictStringParamConcatRector.php',
26062606
'Rector\\TypeDeclaration\\Rector\\Class_\\AddTestsVoidReturnTypeWhereNoReturnRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/AddTestsVoidReturnTypeWhereNoReturnRector.php',
2607+
'Rector\\TypeDeclaration\\Rector\\Class_\\ChildDoctrineRepositoryClassTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ChildDoctrineRepositoryClassTypeRector.php',
26072608
'Rector\\TypeDeclaration\\Rector\\Class_\\MergeDateTimePropertyTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/MergeDateTimePropertyTypeDeclarationRector.php',
26082609
'Rector\\TypeDeclaration\\Rector\\Class_\\PropertyTypeFromStrictSetterGetterRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/PropertyTypeFromStrictSetterGetterRector.php',
26092610
'Rector\\TypeDeclaration\\Rector\\Class_\\ReturnTypeFromStrictTernaryRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/Class_/ReturnTypeFromStrictTernaryRector.php',

0 commit comments

Comments
 (0)