Skip to content

Commit 5b1ef38

Browse files
committed
always add nullable on TypedPropertyFromJMSSerializerAttributeTypeRector as serializer does not use constructor
1 parent 1193412 commit 5b1ef38

4 files changed

Lines changed: 86 additions & 50 deletions

File tree

rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/non_typed_assigned_by_constructor.php.inc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSeriali
2222
final class NonTypedAssignedByConstructor
2323
{
2424
#[\JMS\Serializer\Annotation\Type('string')]
25-
private string $name;
25+
private ?string $name = null;
2626

2727
public function __construct($name)
2828
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
4+
5+
final class NullableIntWithConstructor
6+
{
7+
#[\JMS\Serializer\Annotation\Type('int')]
8+
protected $number;
9+
10+
public function __construct(int $number)
11+
{
12+
$this->number = $number;
13+
}
14+
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
21+
22+
final class NullableIntWithConstructor
23+
{
24+
#[\JMS\Serializer\Annotation\Type('int')]
25+
protected ?int $number = null;
26+
27+
public function __construct(int $number)
28+
{
29+
$this->number = $number;
30+
}
31+
}
32+
33+
?>

rules-tests/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector/Fixture/skip_typed_assigned.php.inc

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
44

5-
final class SkipTypedAssigned
5+
final class ExactTypedAssigned
66
{
77
#[\JMS\Serializer\Annotation\Type('string')]
88
private $name;
@@ -12,3 +12,22 @@ final class SkipTypedAssigned
1212
$this->name = $name;
1313
}
1414
}
15+
16+
?>
17+
-----
18+
<?php
19+
20+
namespace Rector\Tests\TypeDeclaration\Rector\Class_\TypedPropertyFromJMSSerializerAttributeTypeRector\Fixture;
21+
22+
final class ExactTypedAssigned
23+
{
24+
#[\JMS\Serializer\Annotation\Type('string')]
25+
private ?string $name = null;
26+
27+
public function setName(string $name): void
28+
{
29+
$this->name = $name;
30+
}
31+
}
32+
33+
?>

rules/TypeDeclaration/Rector/Class_/TypedPropertyFromJMSSerializerAttributeTypeRector.php

Lines changed: 32 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@
55
namespace Rector\TypeDeclaration\Rector\Class_;
66

77
use PhpParser\Node;
8+
use PhpParser\Node\Expr;
89
use PhpParser\Node\Expr\ConstFetch;
910
use PhpParser\Node\Identifier;
1011
use PhpParser\Node\Name;
1112
use PhpParser\Node\Name\FullyQualified;
1213
use PhpParser\Node\NullableType;
1314
use PhpParser\Node\Stmt\Class_;
15+
use PhpParser\Node\Stmt\Property;
1416
use PHPStan\Reflection\ClassReflection;
1517
use PHPStan\Type\MixedType;
1618
use PHPStan\Type\ObjectType;
17-
use PHPStan\Type\Type;
1819
use Rector\Doctrine\CodeQuality\Enum\CollectionMapping;
1920
use Rector\Enum\ClassName;
2021
use Rector\Php74\Guard\MakePropertyTypedGuard;
@@ -25,8 +26,6 @@
2526
use Rector\Reflection\ReflectionResolver;
2627
use Rector\StaticTypeMapper\Mapper\ScalarStringToTypeMapper;
2728
use Rector\StaticTypeMapper\StaticTypeMapper;
28-
use Rector\TypeDeclaration\AlreadyAssignDetector\ConstructorAssignDetector;
29-
use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer\AllAssignNodePropertyTypeInferer;
3029
use Rector\Util\StringUtils;
3130
use Rector\ValueObject\PhpVersionFeature;
3231
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
@@ -39,14 +38,12 @@
3938
final class TypedPropertyFromJMSSerializerAttributeTypeRector extends AbstractRector implements MinPhpVersionInterface
4039
{
4140
public function __construct(
42-
private readonly AllAssignNodePropertyTypeInferer $allAssignNodePropertyTypeInferer,
4341
private readonly MakePropertyTypedGuard $makePropertyTypedGuard,
4442
private readonly ReflectionResolver $reflectionResolver,
4543
private readonly ValueResolver $valueResolver,
4644
private readonly PhpAttributeAnalyzer $phpAttributeAnalyzer,
4745
private readonly ScalarStringToTypeMapper $scalarStringToTypeMapper,
48-
private readonly StaticTypeMapper $staticTypeMapper,
49-
private readonly ConstructorAssignDetector $constructorAssignDetector
46+
private readonly StaticTypeMapper $staticTypeMapper
5047
) {
5148
}
5249

@@ -96,7 +93,7 @@ public function refactor(Node $node): ?Node
9693
$classReflection = null;
9794

9895
foreach ($node->getProperties() as $property) {
99-
if ($property->type instanceof Node) {
96+
if ($property->type instanceof Node || $property->props[0]->default instanceof Expr) {
10097
continue;
10198
}
10299

@@ -124,39 +121,11 @@ public function refactor(Node $node): ?Node
124121
continue;
125122
}
126123

127-
$inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty(
128-
$property,
129-
$classReflection,
130-
$this->file
131-
);
132-
// has assigned with type
133-
if ($inferredType instanceof Type) {
134-
continue;
135-
}
136-
137-
if ($property->props[0]->default instanceof Node) {
138-
continue;
139-
}
140-
141-
$typeValue = null;
142-
foreach ($property->attrGroups as $attrGroup) {
143-
foreach ($attrGroup->attrs as $attr) {
144-
if ($attr->name->toString() === ClassName::JMS_TYPE) {
145-
$typeValue = $this->valueResolver->getValue($attr->args[0]->value);
146-
break;
147-
}
148-
}
149-
}
150-
124+
$typeValue = $this->resolveAttributeType($property);
151125
if (! is_string($typeValue)) {
152126
continue;
153127
}
154128

155-
if (StringUtils::isMatch($typeValue, '#DateTime\<(.*?)\>#')) {
156-
// special case for DateTime, which is not a scalar type
157-
$typeValue = 'DateTime';
158-
}
159-
160129
// skip generic iterable types
161130
if (str_contains($typeValue, '<')) {
162131
continue;
@@ -174,20 +143,10 @@ public function refactor(Node $node): ?Node
174143
return null;
175144
}
176145

177-
$isInConstructorAssigned = $this->constructorAssignDetector->isPropertyAssigned(
178-
$node,
179-
$this->getName($property)
180-
);
181-
$type = $isInConstructorAssigned ? $propertyType : new NullableType($propertyType);
182-
183-
$property->type = $type;
184-
185-
if (! $isInConstructorAssigned) {
186-
$property->props[0]->default = new ConstFetch(new Name('null'));
187-
}
146+
$property->type = new NullableType($propertyType);
147+
$property->props[0]->default = new ConstFetch(new Name('null'));
188148

189149
$hasChanged = true;
190-
// }
191150
}
192151

193152
if ($hasChanged) {
@@ -196,4 +155,29 @@ public function refactor(Node $node): ?Node
196155

197156
return null;
198157
}
158+
159+
private function resolveAttributeType(Property $property): ?string
160+
{
161+
foreach ($property->attrGroups as $attrGroup) {
162+
foreach ($attrGroup->attrs as $attr) {
163+
if (! $this->isName($attr->name, ClassName::JMS_TYPE)) {
164+
continue;
165+
}
166+
167+
$typeValue = $this->valueResolver->getValue($attr->args[0]->value);
168+
if (! is_string($typeValue)) {
169+
return null;
170+
}
171+
172+
if (StringUtils::isMatch($typeValue, '#DateTime\<(.*?)\>#')) {
173+
// special case for DateTime, which is not a scalar type
174+
return 'DateTime';
175+
}
176+
177+
return $typeValue;
178+
}
179+
}
180+
181+
return null;
182+
}
199183
}

0 commit comments

Comments
 (0)